You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed from #8287 (PR: claude/issue-8287-api-key-organization). Routed to the packages/spec seat because the only file that can resolve it is packages/spec/src/api/error-code-ledger.zod.ts.
Context
#8287 made API keys carry an organization. Two refusals now exist at verify time:
an org-less key under isolated — provably dead there (the wall is organization_id = activeOrganizationId), so it is refused rather than allowed to answer 200 + total 0;
a key whose owner's membership in its organization has ended.
Its follow-through decision asked for these to be refused "with a distinguishable error code".
What shipped instead, and why
Both refusals answer the standard catalog member UNAUTHENTICATED (401). The behavioural requirement is met — the failure is loud at call time instead of a silent empty result — but the two refusals are not distinguishable on the wire from any other unauthenticated request.
Two reasons this was not resolved in that PR, the second of which may mean there is nothing to do here:
ERROR_CODE_LEDGER lives in packages/spec, which that card placed absolutely off-limits to the implementing seat.
A machine-readable discriminator does exist internally: ResolvedAuthzContext.authRefusal.reason ('organization_required' / 'organization_membership_ended'), deliberately lowercase so it cannot be mistaken for a wire code. Nothing currently surfaces it on the wire.
The decision this needs
Either:
(A) leave it — UNAUTHENTICATED is the honest standard member; the operator-facing detail rides in the message. Then this issue closes as "working as the ledger intends", and the value is having the question on the record.
(B) register API_KEY_ORGANIZATION_REQUIRED (and possibly a membership sibling) with a STANDARD_SYNONYM_WAIVERS entry recording what it shadows and why — justified by the operator-debugging case: an automation that breaks after a posture change or a membership removal currently reports the same 401 as a missing header.
Worth noting for (B): the deployments most affected are running isolated with existing org-less keys, where every such key starts refusing at once — a distinguishable code is exactly what turns that from a mystery into a one-line diagnosis.
Filed from #8287 (PR:
claude/issue-8287-api-key-organization). Routed to thepackages/specseat because the only file that can resolve it ispackages/spec/src/api/error-code-ledger.zod.ts.Context
#8287 made API keys carry an organization. Two refusals now exist at verify time:
isolated— provably dead there (the wall isorganization_id = activeOrganizationId), so it is refused rather than allowed to answer200 + total 0;Its follow-through decision asked for these to be refused "with a distinguishable error code".
What shipped instead, and why
Both refusals answer the standard catalog member
UNAUTHENTICATED(401). The behavioural requirement is met — the failure is loud at call time instead of a silent empty result — but the two refusals are not distinguishable on the wire from any other unauthenticated request.Two reasons this was not resolved in that PR, the second of which may mean there is nothing to do here:
ERROR_CODE_LEDGERlives inpackages/spec, which that card placed absolutely off-limits to the implementing seat.@objectstack/restregisters four generic synonyms the standard catalog already covers (CONFLICT,NOT_FOUND,FORBIDDEN,INTERNAL) — contract call, not a cleanup #8211 that is mechanical, withstandardSynonymOfrefusing a new code that shadows a standard member unless aSTANDARD_SYNONYM_WAIVERSentry records why. A refused credential answering 401 is a strong candidate for exactly that refusal.A machine-readable discriminator does exist internally:
ResolvedAuthzContext.authRefusal.reason('organization_required'/'organization_membership_ended'), deliberately lowercase so it cannot be mistaken for a wire code. Nothing currently surfaces it on the wire.The decision this needs
Either:
UNAUTHENTICATEDis the honest standard member; the operator-facing detail rides in the message. Then this issue closes as "working as the ledger intends", and the value is having the question on the record.API_KEY_ORGANIZATION_REQUIRED(and possibly a membership sibling) with aSTANDARD_SYNONYM_WAIVERSentry recording what it shadows and why — justified by the operator-debugging case: an automation that breaks after a posture change or a membership removal currently reports the same 401 as a missing header.Worth noting for (B): the deployments most affected are running
isolatedwith existing org-less keys, where every such key starts refusing at once — a distinguishable code is exactly what turns that from a mystery into a one-line diagnosis.