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
The second "Related, not the same" item from #3689, filed on its own now that #3837 has closed that issue. Blocked on #3841 (which vocabulary the code should be written in) — fixing this one first would mean picking a dialect by accident.
The drift
HttpDispatcher.error() (packages/runtime/src/http-dispatcher.ts:451) takes the HTTP status as its code parameter and writes it straight into the field ApiErrorSchema reserves for a semantic string:
toEqual(['code']) is deliberately exact: a second deviating field fails it, and so does fixing the dispatcher — at which point the pin should be deleted rather than updated.
The reason for pinning was blast radius: error.code is read by the SDK's error path, by the console, and by the dogfood suite. That has not changed, so this is still a real change with a consumer sweep, not a rename.
Rename the number to httpStatus and free code.EnhancedApiErrorSchema already declares an httpStatus field, so this has precedent in the spec. Same consumer sweep, but the number stays available for anyone reading it today.
Change the spec to match — declare code as z.union([z.string(), z.number()]). Cheapest, and wrong: it blesses two meanings for one field and gives the conformance suites nothing to assert.
Recommendation: option 2 then option 1 — introduce httpStatus, move the semantic codes into code, keep details for genuine context only. Doing it in that order means no window where a caller has neither field.
Scope note
This is the dispatcher only. The autonomously-mounted service routes (storage-routes.ts, i18n-service-plugin.ts) already emit a semantic string in error.code as of #3687 — they were never the problem here. Fixing the dispatcher is what finally makes the two providers of the same SDK method agree on what error.codemeans, which is the last piece of the asymmetry #3636 → #3675 → #3689 has been closing one layer at a time.
The second "Related, not the same" item from #3689, filed on its own now that #3837 has closed that issue. Blocked on #3841 (which vocabulary the code should be written in) — fixing this one first would mean picking a dialect by accident.
The drift
HttpDispatcher.error()(packages/runtime/src/http-dispatcher.ts:451) takes the HTTP status as itscodeparameter and writes it straight into the fieldApiErrorSchemareserves for a semantic string:ApiErrorSchema(packages/spec/src/api/contract.zod.ts) declares:So
error.codecomes back as400/403/503— a number, duplicatingresponse.status, and occupying the field a caller is meant to branch on.The dispatcher already works around its own field being taken, which is the tell that the number is in the wrong place:
http-dispatcher.ts:679details.code—this.error(gate.message, 403, { code: gate.code })http-dispatcher.ts:1379details.code—{ code: 'PERMISSION_DENIED', …e.details }routeNotFound()(:485)error.type— a third spelling,'ROUTE_NOT_FOUND', sibling to the numericcodedetails.type—'PROJECT_MEMBERSHIP_REQUIRED'Four sites, three different parking spots (
details.code,details.type,error.type), because the declared one is full.Why it was pinned rather than fixed
#3687 chose to nail the deviation down to exactly one field instead of moving it, and said so in
http-dispatcher.test.ts:toEqual(['code'])is deliberately exact: a second deviating field fails it, and so does fixing the dispatcher — at which point the pin should be deleted rather than updated.The reason for pinning was blast radius:
error.codeis read by the SDK's error path, by the console, and by the dogfood suite. That has not changed, so this is still a real change with a consumer sweep, not a rename.What needs deciding
error.code, drop the number. The status is already on the HTTP response and inmeta; duplicating it into the body's one semantic slot buys nothing. Everydetails.code/details.type/error.typeparking spot collapses into the declared field. Requires Two error-code vocabularies are both live:StandardErrorCodeis lowercase snake_case, the servers emit SCREAMING_SNAKE #3841 first (which dialect), plus a sweep of readers that currently expect a number there.httpStatusand freecode.EnhancedApiErrorSchemaalready declares anhttpStatusfield, so this has precedent in the spec. Same consumer sweep, but the number stays available for anyone reading it today.codeasz.union([z.string(), z.number()]). Cheapest, and wrong: it blesses two meanings for one field and gives the conformance suites nothing to assert.Recommendation: option 2 then option 1 — introduce
httpStatus, move the semantic codes intocode, keepdetailsfor genuine context only. Doing it in that order means no window where a caller has neither field.Scope note
This is the dispatcher only. The autonomously-mounted service routes (
storage-routes.ts,i18n-service-plugin.ts) already emit a semantic string inerror.codeas of #3687 — they were never the problem here. Fixing the dispatcher is what finally makes the two providers of the same SDK method agree on whaterror.codemeans, which is the last piece of the asymmetry #3636 → #3675 → #3689 has been closing one layer at a time.