Skip to content

fix: surface response encoding errors instead of bare 500 {} - #275

Draft
ralph-bitgo[bot] wants to merge 2 commits into
masterfrom
WCN-2396-encode-error-diagnostics
Draft

ralph-bitgo[bot] wants to merge 2 commits into
masterfrom
WCN-2396-encode-error-diagnostics

Conversation

@ralph-bitgo

@ralph-bitgo ralph-bitgo Bot commented Oct 5, 2026

Copy link
Copy Markdown

What

  • Add customEncodeErrorFormatter in src/shared/errorFormatters.ts: when a
    response payload fails its io-ts codec check in typed-express-router's
    res.sendEncoded(), it logs the real error (message, stack, and request
    context: method, URL, api name) and returns
    { error: 'Internal Server Error', details: <real message> } in the 500
    body — matching the documented 500 ErrorResponse shape.
  • Wire encodeErrorFormatter: customEncodeErrorFormatter into every
    createRouter call site alongside the existing decode formatter: the MBE
    API router, both MBE health routers, and the AWM API/health routers.
  • Regression tests: an end-to-end test in generateWallet.test.ts makes
    wallet/add return a wallet omitting a required field (as WCN-2395-induced
    responses do) and asserts a descriptive 500 body plus a server-side log
    line; this test fails with the bare 500 {} on pre-fix code. Unit tests in
    errorFormatters.test.ts pin the formatter contract (log content and body
    shape, including the non-Error fallback).

Why

Customers creating type: advanced wallets hit the WCN-2395 permission bug
(creator stuck at view-only because UMS rejects walletType: advanced): the
wallet is created on the backend, but the /wallet/add response then violates
MBE's response schema, and typed-express-router silently swaps the real
validation error for a bare HTTP 500 {} — no error body, no server log,
nothing actionable for the customer or for on-call (originally reported by
AKM as a secondary symptom of WCN-2395). The library's default
encodeErrorFormatter is () => ({}) and MBE only overrode the decode-side
formatter, so the encode side stayed on library defaults.

This is the ticket's immediate/diagnostic fix: every future schema-violating
response becomes debuggable — the client sees which response codec failed and
operators get the full error with stack in the logs — independent of the
upstream WCN-2395 fix. Once WCN-2395 lands upstream, the original symptom
disappears on its own, but this diagnostic layer stays valuable for any
future response-shape regression.

The root fix (UMS enum + wallet-platform retry/DLQ handling) lives in
WCN-2395 and is out of scope for this repo. I deliberately did not relax the
required fields in generateWalletRoute.ts to t.partial: the exact
mismatching field still needs live confirmation, and weakening the declared
contract would mask future server-side bugs.

Test plan

  • npm test — full suite: 442 passing, including the new regression test
    (verified it fails with the bare 500 {} against pre-fix wiring) and
    the formatter unit tests
  • npm run lint — clean
  • npm run build (tsc) — clean
  • scripts/check-openapi-masterExpress.sh — OpenAPI spec in sync (no
    route definitions changed)

Ticket: WCN-2396

typed-express-router validates every outbound response against its
io-ts codec in res.sendEncoded() and falls back to encodeErrorFormatter
when the payload fails that check. The library default swallows the
validation error and replies with HTTP 500 and an empty JSON body,
which is what customers hit from POST /wallet/generate while the
WCN-2395 permission bug left advanced-wallet creators view-only: the
wallet was created on the backend, but MBE returned a bare 500 {} with
nothing actionable in the response or the server logs.

Add a customEncodeErrorFormatter that logs the real error (message,
stack, and request context) and returns { error, details } in the
response body, matching the documented 500 ErrorResponse shape, and
wire it into every typed router (MBE, AWM, and the health check
routers) alongside the existing decode error formatter.

Ticket: WCN-2396
Session-Id: 055eae11-5e57-4df7-8a1e-f8148946b128
Task-Id: 0aaa81ad-1de6-4466-96fb-681ce47e5a15
Cover the new encode error formatter both end to end and at unit
level: a wallet/add response that omits a required field (as produced
by the WCN-2395 permission bug) must produce a 500 with a descriptive
{ error, details } body plus a server-side log line naming the failed
route, instead of the bare 500 {} MBE previously returned. The
end-to-end test reproduces the bare-500 failure against the pre-fix
router wiring.

Ticket: WCN-2396
Session-Id: 055eae11-5e57-4df7-8a1e-f8148946b128
Task-Id: 0aaa81ad-1de6-4466-96fb-681ce47e5a15
@linear-code

linear-code Bot commented Oct 5, 2026

Copy link
Copy Markdown

WCN-2396

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants