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
{{ message }}
Repository navigation
service-i18n error responses are bare { error }, not the declared { success: false, error: {...} } envelope #3675
Found while ledgering the autonomously-mounted i18n surface for #3636. That PR fixed the success envelope; this is the other half of the same drift, left out deliberately so the fix stayed scoped to what unwrapResponse keys on.
The drift
The i18n route group declares the response_envelope middleware (plugin-rest-api.zod.ts), and BaseResponseSchema (contract.zod.ts) defines the shape as { success, error?, meta? }. The dispatcher's /i18n domain emits exactly that via its error() helper (http-dispatcher.ts):
service-i18n emits a bare object instead — five sites in i18n-service-plugin.ts:
Line
Body
170
{ error: error.message } (500)
179
{ error: 'Missing locale parameter' } (400)
185
{ error: error.message } (500)
195
{ error: 'Missing object or locale parameter' } (400)
218
{ error: error.message } (500)
So the same SDK method gets two different error shapes depending on which provider mounted the route — the exact asymmetry #3636 fixed on the success path, still live on the error path. A caller that reads body.error.message works against the dispatcher and gets undefined against service-i18n; one that reads body.error as a string works the other way round.
ObjectStackClient.unwrapResponse keys on the success flag, so only the success bodies were breaking the SDK's declared return type. Widening to error bodies would have been scope creep into a route-audit PR, and unlike the success fix it is not purely additive: error changes from string to object, so anything reading it as a string breaks.
What needs deciding
Align to the contract — { success: false, error: { message, code } } at all five sites. Correct per Prime Directive Add comprehensive test suite for Zod schema validation #12 (fix the producer, not the consumer), and matches the dispatcher. Needs a sweep for readers that assume error is a string; storage-routes.ts uses the same bare shape, so if this is done it should probably be done for both services at once rather than leaving a new inconsistency.
Leave it and document the bare shape as intentional for autonomously-mounted service routes — cheaper, but it means the declared response_envelope middleware is not what these routes actually do, which is a declared ≠ enforced gap of its own.
Recommendation: option 1, covering service-i18n and service-storage together, with a check on @object-ui error-surfacing paths first — the storage upload/download errors are the ones a user is most likely to see rendered.
Note this is not something the route ledgers can catch: they audit which routes exist and whether the SDK can address them, not what the bodies look like. Worth knowing as a limit of the #3563 audit family rather than an oversight in it.
Activity
docs(wire-format): note that service-mounted routes answer in the dec…
fix(service-storage,service-i18n): emit the declared error envelope, …
fix(service-storage): emit the declared success envelope on all eight…
refactor(qa)!: replace the route-envelope package with a repo-wide ch…
fix(rest,service-settings,service-datasource)!: four more route modul…
refactor(qa)!: replace the route-envelope package with a repo-wide ch…
refactor(service-i18n): collapse the four inline success builders beh…
fix(plugin-sharing)!: the share-link routes emit the declared envelop…
fix(service-datasource,rest): the last three uncovered datasource rou…
fix(api): envelope the plugin-mounted Hono error paths — six refusal …