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:

registrar routes arms still on a retired dialect
registerSecurityExplainEndpoints GET/POST /api/v1/security/explain, GET /api/v1/security/my-delegable-scope 401 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

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions