No rows inside scope
Filters removed every row
Permissions hid the rows
Errors are not empty results
The sentence names which one
Zero rows are not one fact
“East China had no orders last week” and “you are not allowed to see East China orders” can both return zero rows. The first is a business fact. The second is an authorization result. If a model says both mean there was no business, a regional manager will investigate stock and campaigns, while security never learns that a probe happened. Classify the empty result before writing the sentence.
At least three classes exist: no matching rows inside the current grant; a time, status, or dimension filter removed rows that do exist; row or column security hid rows, and the caller must not learn whether anything exists outside scope. An empty table and a permission denial are different database outcomes. The query layer should not collapse them into “no data found.”
Count inside the grant, then explain filters
Bind the current identity, apply the row scope, then see how many rows the time window, status, and dimension filters each remove. If the grant itself is empty, say “nothing is visible to you in that window.” If dropping one filter produces rows, name that filter and the relaxed count. Do not silently change the filter and present a new conclusion.
Microsoft’s row-level security guidance says to test with the real identity and the real context, not only a role name. A shared service account that scans the company and then deletes rows in the application mixes “the company has nothing” with “this person cannot see it.” Permission filters belong before the rows ever reach the model.
No access and no rows need different wording
For a missing grant, the safe sentence is “this identity cannot query that scope,” not “that scope has no data.” The second sentence leaks one bit: the outside scope is empty. Hidden amounts with a visible customer list are not the same as hidden rows. Say which columns are masked. Follow-up questions that change regions until “none” becomes “not allowed” belong in the audit log, not in ordinary clarification.
Timeouts, missing semantic versions, and connection failures are not zero-row successes. Calling a failure “no orders” stops operators from retrying. A successful SELECT may return zero rows; an execution error is another path. The tool should pass that status to the answer layer and forbid an empty business conclusion after a failure.
Two implementation paths
Path one returns diagnostic counts every time: rows in the grant, rows after the time window, rows after the final filter. It suits analysts and data on-call, because they can see whether security or a filter caused the empty set. Those counts must stay inside a set the caller is already allowed to see. A diagnostic that reveals “many rows exist outside your scope” is its own leak.
Path two returns one classified sentence to business users and keeps the counts in the audit log. It suits stores and sales, if the wording for “not allowed” never drifts into “none.” The model must not add, from memory, that a neighboring region actually had volume.
Counterexamples and acceptance
Counterexamples: zero rows after a security filter, narrated as no sales; a filter for “closed” that does not match the stored value CLOSED, then explained as a shutdown; a timeout that still prints zero. Replay one question as three identities: a manager with rows, a manager whose scope is truly empty, and an account with no grant. The sentences, counts, and audit events should differ.
A follow-up that drops the closed-status filter must recompute inside the same grant and show the new filter. It must not reuse the previous word “none.” AskTable.ai can be a candidate entry point for enterprise queries. This article does not show that the current release already separates true emptiness, over-filtering, and missing access. Verify it with your own identities.
Public references
Ready to help your team start?
Talk through a real scenario and see how AskTable.ai can fit your business.
Book a demo