Parent: #9487 (found during that card's Zone-2 producer sweep; filed unassigned for triage). The #9487 maintainer ruling — add code: "UNAUTHENTICATED" alongside the existing error / message keys, additive only — is the authority for the fix shape here too.
Observed
packages/runtime/src/dispatcher-plugin.tsmountRouteOnServer (the 401 arm at lines 213–221 on main @ 4c260cda5) writes an inline copy of the flat anonymous-deny body:
res.status(401);// ...res.json({error: 'UNAUTHENTICATED',message: 'Authentication is required to access this endpoint.',});It does not reference ANONYMOUS_DENY_BODY, so the #9487 constant change does not reach it. A repo-wide sweep for the deny message finds exactly two non-test producers: the @objectstack/core constant (fixed by #9487's PR) and this inline copy.
Impact
Service-declared endpoint routes (RouteDefinition emitted via hooks, e.g. buildAIRoutes() — any route with auth not explicitly false) answer an anonymous caller 401 { error, message } with no code key. Through @objectstack/client, err.code stays undefined for those 401s — the exact defect #9487 fixed for the REST enforceAuth seam. #9487's acceptance property ("one documented key identifies the machine code on every error family, 401 included") is not universal until this site moves.
Fix shape (per the #9487 ruling — additive only)
The file already imports from @objectstack/core, and the shared constant exists precisely so seams cannot drift: replace the inline literal with res.json(ANONYMOUS_DENY_BODY) (plus ANONYMOUS_DENY_STATUS for the status), or minimally add code: 'UNAUTHENTICATED'. Additive only — no key removed or moved.
Notes for the implementer
Generated by Claude Code
Parent: #9487 (found during that card's Zone-2 producer sweep; filed unassigned for triage). The #9487 maintainer ruling — add
code: "UNAUTHENTICATED"alongside the existingerror/messagekeys, additive only — is the authority for the fix shape here too.Observed
packages/runtime/src/dispatcher-plugin.tsmountRouteOnServer(the 401 arm at lines 213–221 onmain@4c260cda5) writes an inline copy of the flat anonymous-deny body:It does not reference
ANONYMOUS_DENY_BODY, so the #9487 constant change does not reach it. A repo-wide sweep for the deny message finds exactly two non-test producers: the@objectstack/coreconstant (fixed by #9487's PR) and this inline copy.Impact
Service-declared endpoint routes (
RouteDefinitionemitted via hooks, e.g.buildAIRoutes()— any route withauthnot explicitlyfalse) answer an anonymous caller401 { error, message }with nocodekey. Through@objectstack/client,err.codestaysundefinedfor those 401s — the exact defect #9487 fixed for the RESTenforceAuthseam. #9487's acceptance property ("one documented key identifies the machine code on every error family, 401 included") is not universal until this site moves.Fix shape (per the #9487 ruling — additive only)
The file already imports from
@objectstack/core, and the shared constant exists precisely so seams cannot drift: replace the inline literal withres.json(ANONYMOUS_DENY_BODY)(plusANONYMOUS_DENY_STATUSfor the status), or minimally addcode: 'UNAUTHENTICATED'. Additive only — no key removed or moved.Notes for the implementer
dispatcher-plugin.endpoint-fallback.integration.test.tsasserts a nestedbody.error.code, i.e. it exercises the http-dispatcher path, not this arm. Re-derive which path serves what before writing pins, and add a pin for this arm's exact body.packages/qa/dogfood/test/showcase-anonymous-deny-surfaces.dogfood.test.ts,isRestFlatDeny) tolerates the additivecodekey — measured on a real booted showcase in Every 401 answers{error, message}with nocodekey, while every other error family carries{error, code}#9487's PR (25/25 green).Generated by Claude Code