Skip to content

Three more sites report an availability failure as a definitive negative (permission-fold fault → "Access denied", read timeout → "Not found", cold cache → "user unknown") #974

Description

@rbuergi

Summary

Split out of #637. PR #947 fixed the API-token leg and PR #970 fixes the identity/authorization leg, but three more sites collapse an availability failure into a definitive negative answer — the same defect class, each needing its own change.

An availability failure answered as a definitive negative is worse than an error, because it is actionable-looking: the user is told something specific and false, and acts on it. #637's headline case was a storage stall telling a signed-in user they were not onboarded and redirecting them to the sign-up form.

The remaining three

Site Reports a … As
AccessControlPipeline permission-fold fault "Access denied"
MeshOperations.FetchNode read timeout "Not found"
UserIdentityCache cold cache "user unknown"

Each is a lie with a different cost. "Access denied" sends a correctly-entitled user to ask for permissions they already have. "Not found" invites deleting or recreating a node that exists. "User unknown" is the same trap #637 opened — it can drive onboarding or provisioning logic for a user who is already onboarded.

Why these are not a quick follow-up to #970

Unlike the identity reads, these cannot be fixed by classifying at a single bounded seam. Each needs either a tri-state carried through the permission fold (so "we could not determine" survives the fold instead of collapsing into "no") or a new ErrorType on the bus so the condition can cross a hub boundary without being flattened. That is why #970 deliberately stops short of them rather than reaching for a partial fix.

The rule these should follow

Decide the distinction where the condition is known — inside the one place a budget or a fold is applied — and never reconstruct it upstream by sniffing a message string. That is the shape #970 uses (IdentityReadOutcome<T> decided inside IdentityRead.Bounded), and it is what makes the classification survive rewording.

Also worth applying #970's lesson about its own fix: after removing a catch, sweep for edges that can now throw past the classifier. That sweep found a third instance in #970 itself — LoadDbRolesAsync resolving the hub and workspace outside the classified chain, where a hub mid-disposal would have escaped as a 500.

Not band-aids

Widening a timeout, retrying, or catching-and-defaulting are all excluded: the goal is to name the condition correctly, not to make it rarer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions