Skip to content

[finding] the two registrars ADJACENT to #7981 still answer the retired flat / bare-string error dialects (ADR-0112) #8073

Description

@hotlong

Observation-class finding, recorded while fixing #7981 (PR #8071). Not fixed there — that card was scoped to the three /security/suggested-bindings routes by explicit ruling, and these are separate registrars. Filed unassigned.

Fact

With #7981 landed, registerSecurityEndpoints speaks the ADR-0112 D5 envelope { error: { code, message } } on every arm. Its two immediate neighbours in packages/rest/src/rest-server.ts do not, and they carry the same two dialects #7035 (PR #7293) retired from this file's /meta refusals:

registrarroutesarms still on a retired dialect
registerSecurityExplainEndpointsGET/POST /api/v1/security/explain, GET /api/v1/security/my-delegable-scope401 UNAUTHORIZED, 501 NOT_IMPLEMENTED, 400 VALIDATION_FAILED, 403 PERMISSION_DENIED are all flat { code, message }; the two 500s are { code, error: 'a bare string' }
registerSharingEndpoints (respondSharingError)the /sharing/* family{ code, error: msg.replace(...) } — bare-string error, one emitter feeding 400 / 403 / 404 / 409 / 422

So body.error.code, the one position ADR-0112 D5 declares, reads undefined on all of them, and /security/explain is directly adjacent to the routes that no longer behave that way — a client calling explain and then suggested-bindings now meets two shapes across one security family.

Why it is worth recording separately

registerSecurityExplainEndpoints is not dormant: the client SDK exposes it as client.security.explain and client.security.describeDelegableScope (packages/client/src/index.ts), and content/docs/permissions/* documents the route for operators. security-routes.test.ts currently PINS the flat shape (expect(res.body.code).toBe('NOT_IMPLEMENTED'), ...('PERMISSION_DENIED'), ...('EXPLAIN_FAILED')), so those pins move with any convergence — the same fixture-triage step #7981 needed.

The same wire-visibility question #7981 had applies and has the same measured answer: the SDK's error path in ObjectStackClient.fetch reads BOTH envelopes' declared spots (errorBody?.code ?? errorBody?.error?.code, and a typeof errorBody?.error === 'string' limb for the message), so a converged body keeps producing identical err.code / err.message. That measurement is recorded on #7981 and should be re-run, not assumed, for whatever consumers these two families have.

Not claimed here

Suggested handling

Triage-level, not decided here: converge each registrar onto { error: { code, message } } through a single emitter, exactly as PR #8071 did for registerSecurityEndpoints — the shape is already ruled by ADR-0112 D5, so this is implementation of an existing decision. The explain pair is the smaller, higher-adjacency half and is a natural first card.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions