Connect by JBRH Open Connect

A row with no workspace stamp is invisible, not missing

A record that is not on the screen has not necessarily gone anywhere. In a workspace-scoped system the commonest cause is a predicate that excludes it — most often a stamp of '', which matches no scope and no row-level security policy. The signature is an asymmetry: every list omits the row, and every action by id on it still works. That distinction decides the repair.

Status
Available What this means
Audience
both, developer
Last verified
Product version
6.3.2

Three words that are not synonyms#

Missing
The row does not exist. Something failed before it was written, or it was hard-deleted. This is the only one of the three where a backup is part of the answer.
Deleted
The row exists and a person put it out of sight — threads.deleted_at and its equivalents on calls. Reversible, deliberate, and the person's own decision to make.
Invisible
The row exists, holds live data, and is excluded by a predicate. Nothing failed and nothing raised. Restoring a backup here creates a second copy of a record that was never gone.

Choosing between them by instinct is how one hidden row becomes two live ones. The case that made this concrete: two connected mail identities vanished from a screen with no duplicate warning, no error and no failing request — and the uniqueness check that should have prevented a reconnection could not see them either, so connecting again would have written a second row for one real inbox, both holding credentials.

The triage, in about a minute#

  1. Fetch the record by its identifier — a link, a permalink, an action that takes an id.

    Result If it answers, the row exists and this is a visibility problem. Nothing needs restoring, and the incident is now about a predicate rather than about data loss.

  2. Ask, read-only, how many rows in that table carry an empty stamp rather than a NULL one.

    Result The two are different faults. A NULL usually means a column was added without a backfill; an empty string usually means something wrote a default it never intended. The counts tell you which story you are in.

  3. Check the uniqueness path for the same key.

    Result If the key reads as free while a hidden row holds it, a duplicate is one user action away and the screen is actively inviting one. That is the urgent half of the incident.

  4. Only then consider a backup, and only if step one found nothing.

    Result A restore against an invisible row is how a support ticket becomes a data problem.

What else hides a row that exists#

CauseHow it presentsWhere to look
Empty workspace stampLists omit it; by-id works; no error anywhereThe read-only count per scoped table
A soft deleteGone from the default view, present in the deleted viewdeleted_at on the record
A capability the session may not callThe screen is empty or a request is refused with 403The customer_safe allowlist and tenantAdapt
A count that stopped at the page sizeRows exist, the total is wrong, a later page is emptyHeadline numbers that stop at the page size
A table absent from SCOPED_TABLESThe opposite symptom — rows appear that should notThe scoped-table list, and whether a policy exists at all

The last row is worth sitting with. The same class of oversight produces both invisibility and over-visibility, depending on which half is missing: a stamp that matches nothing hides a row from its owner, and a table that nothing filters shows rows to everybody.

Why the fix is not a quick update#

The instinct is to stamp the row as soon as something notices. That write is refused: the policy's WITH CHECK will not allow a row to be updated into a scope it is not already in, so an attempt from a request turns a quietly hidden row into a server error on somebody's screen. The permission that would allow it is not one a request should hold.

So the repair belongs to the schema owner and runs where schema changes already run — migrate.backfill_canonical_workspace, on every boot, on the connection that legitimately writes across scopes. The mechanics of that repair are written up in a row with an empty workspace id; this page is about the decision that comes before it.

  • A NOT NULL constraint prevents the next one and finds none of the existing ones. Repair first, constrain afterwards, or the migration fails on the rows you were trying to fix.
  • The read-only count proves how many rows are hidden now. UNKNOWN: it cannot say when each was written or by which path, because nothing recorded a fault at the time.
  • Control-plane tables cannot hold this defect — platform identity, sessions, billing and website enquiries are outside every workspace deliberately.

Questions#

Is the data lost?

Almost never, in this failure. The row is present and complete; a predicate excludes it. Fetching it by identifier settles the question in seconds, and if that works, no backup is involved in the repair.

Why was there no error?

Because from the query's point of view nothing went wrong. It asked for rows in this workspace and was correctly told there were none. Both the ORM filter and the database policy agreed, which is why a second enforcement layer does not catch this one.

Can this happen to conversations or contacts as well as mailboxes?

Every table in SCOPED_TABLES can hold it — the defect is in the stamp, not in the record type. It surfaced on mailboxes because a mailbox is something people count, so two of them going quiet was noticed.