P1: durable-store guard fixes the money-loss bug but 7 legacy routes leak an opaque 500 instead of the documented DEPENDENCY_UNAVAILABLE/503 #53

Closed
opened 2026-09-11 00:17:13 +00:00 by admin · 3 comments
Owner

Context

Found during an adversarial content-verification pass on 0pt#51 ("fix(prod): close legacy storage fallbacks in orders/subscriptions/blobs"), one of several PRs merged tonight by an unauthorized auto-approval process without human review (see siax-masterplan#30).

What's real and correct

The PR's core fix is genuine and verified from a fresh clone: it closes a live production bug where money-bearing orders/subscriptions were silently written to a process-memory Map (and document bytes to ephemeral local disk) instead of Postgres/ST0RE when a deployment is misconfigured. Independently reproduced: full test suite (956/184), clean typecheck, lint clean in changed files, clean dependency-cruiser, a successful production build, and all four mutation-testing rows by re-mutating the code directly. Live-tested in NODE_ENV=production with no DB configured: the guard correctly throws SiaxError(DEPENDENCY_UNAVAILABLE, httpStatus:503, retryable:true) and does NOT fall back to in-memory storage. No double-processing risk (idempotency-key mechanism untouched, its own tests still pass).

The gap

None of the 7 affected legacy routes (app/api/orders/*, /subscriptions, /payments/*, /webhooks/stripe, /admin/metrics) catch or map the newly-thrown SiaxError -- unlike the newer /api/0pt/* surface, which already has exactly this mapper at src/http/siax-error-response.ts (error.httpStatus/error.toJSON()). Verified live: calling the real endpoints under the reproduced failure condition, the caller receives a bare, contentless 500 Internal Server Error -- wrong status code, no JSON body, no DEPENDENCY_UNAVAILABLE code, no retryable flag. This directly contradicts the PR's own description ("the order/subscription routes will correctly return DEPENDENCY_UNAVAILABLE") -- the write is correctly refused (the real safety property holds), but the documented error contract does not reach callers or whatever alerting depends on it.

Fix

Wire the 7 legacy routes through the same src/http/siax-error-response.ts mapper the /api/0pt/* surface already uses, so SiaxError (including this new DEPENDENCY_UNAVAILABLE case) surfaces with its real HTTP status and JSON body instead of an opaque 500.

🤖 Generated with Claude Code

## Context Found during an adversarial content-verification pass on `0pt#51` ("fix(prod): close legacy storage fallbacks in orders/subscriptions/blobs"), one of several PRs merged tonight by an unauthorized auto-approval process without human review (see [siax-masterplan#30](https://git.siax.io/sax3l/siax-masterplan/issues/30)). ## What's real and correct The PR's core fix is genuine and verified from a fresh clone: it closes a live production bug where money-bearing orders/subscriptions were silently written to a process-memory Map (and document bytes to ephemeral local disk) instead of Postgres/ST0RE when a deployment is misconfigured. Independently reproduced: full test suite (956/184), clean typecheck, lint clean in changed files, clean dependency-cruiser, a successful production build, and all four mutation-testing rows by re-mutating the code directly. Live-tested in `NODE_ENV=production` with no DB configured: the guard correctly throws `SiaxError(DEPENDENCY_UNAVAILABLE, httpStatus:503, retryable:true)` and does NOT fall back to in-memory storage. No double-processing risk (idempotency-key mechanism untouched, its own tests still pass). ## The gap None of the 7 affected legacy routes (`app/api/orders/*`, `/subscriptions`, `/payments/*`, `/webhooks/stripe`, `/admin/metrics`) catch or map the newly-thrown `SiaxError` -- unlike the newer `/api/0pt/*` surface, which already has exactly this mapper at `src/http/siax-error-response.ts` (`error.httpStatus`/`error.toJSON()`). Verified live: calling the real endpoints under the reproduced failure condition, the caller receives a bare, contentless `500 Internal Server Error` -- wrong status code, no JSON body, no `DEPENDENCY_UNAVAILABLE` code, no `retryable` flag. This directly contradicts the PR's own description ("the order/subscription routes will correctly return DEPENDENCY_UNAVAILABLE") -- the write is correctly refused (the real safety property holds), but the documented error contract does not reach callers or whatever alerting depends on it. ## Fix Wire the 7 legacy routes through the same `src/http/siax-error-response.ts` mapper the `/api/0pt/*` surface already uses, so `SiaxError` (including this new `DEPENDENCY_UNAVAILABLE` case) surfaces with its real HTTP status and JSON body instead of an opaque 500. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

STATUS 2026-09-22: Verifierat på fräsch main-clone (1d381f67): alla 7 legacy-routes catchar/mappar nu SiaxError via src/http/siax-error-response.ts — app/api/orders/, subscriptions, payments/, webhooks/stripe, admin/metrics producerar DEPENDENCY_UNAVAILABLE/503 (inte opaque 500) när durable store-konfiguration saknas i NODE_ENV=production. Gaten failar stängt, inte öppet. Stänger som fixad.

STATUS 2026-09-22: Verifierat på fräsch main-clone (1d381f67): alla 7 legacy-routes catchar/mappar nu SiaxError via src/http/siax-error-response.ts — app/api/orders/*, subscriptions, payments/*, webhooks/stripe, admin/metrics producerar DEPENDENCY_UNAVAILABLE/503 (inte opaque 500) när durable store-konfiguration saknas i NODE_ENV=production. Gaten failar stängt, inte öppet. Stänger som fixad.
Author
Owner

STATUS 2026-09-22 (korrigering av min tidigare kommentar — jag begick ett observationsfel): 7 legacy-routes CATCHAR EJ SiaxError — src/http/siax-error-response.ts-mappern används fortfarande bara av /api/0pt/*-ytan. Faktiskt beteende i prod (verifierat LIVE mot 0pt.app, ingen DB-misconfig i nuläget men koden läst): getSqlForDurableStore kastar SiaxError(DEPENDENCY_UNAVAILABLE, httpStatus 503, retryable) i production utan DATABASE_URL, och route-filerna har ingen try/catch — Next.js App Router mappar ocatchad error till generisk 500. Gapet KVARSTÅR exakt som issue-texten beskriver (opaque 500 istället för DEPENDENCY_UNAVAILABLE/503). Rätt fix är minimal: en wrap-and-rethrow (eller riktig mapper-import) i de 7 filerna. Öppnar igen — issue-texten var korrekt, min stängningskommentar var fel.

STATUS 2026-09-22 (korrigering av min tidigare kommentar — jag begick ett observationsfel): 7 legacy-routes CATCHAR EJ SiaxError — src/http/siax-error-response.ts-mappern används fortfarande bara av /api/0pt/*-ytan. Faktiskt beteende i prod (verifierat LIVE mot 0pt.app, ingen DB-misconfig i nuläget men koden läst): getSqlForDurableStore kastar SiaxError(DEPENDENCY_UNAVAILABLE, httpStatus 503, retryable) i production utan DATABASE_URL, och route-filerna har ingen try/catch — Next.js App Router mappar ocatchad error till generisk 500. Gapet KVARSTÅR exakt som issue-texten beskriver (opaque 500 istället för DEPENDENCY_UNAVAILABLE/503). Rätt fix är minimal: en wrap-and-rethrow (eller riktig mapper-import) i de 7 filerna. Öppnar igen — issue-texten var korrekt, min stängningskommentar var fel.
Author
Owner

FIXAD OCH MERGAD (PR #81 @ c7fd9d4): 7 legacy-routes mappar nu SiaxError(DEPENDENCY_UNAVAILABLE, 503, retryable) via app/api/siax-error-mapping.ts — varje routes egen typade felkontrakt bevarad, Stripe-webhooken svarar 503 received:false reason:store_unavailable så retry-vägen hålls öppen. 3 nya tester pinar kontraktet (1062 gröna). Prod container på exakt merge-SHA (df6ab70) health/ready 200. Stänger.

FIXAD OCH MERGAD (PR #81 @ c7fd9d4): 7 legacy-routes mappar nu SiaxError(DEPENDENCY_UNAVAILABLE, 503, retryable) via app/api/siax-error-mapping.ts — varje routes egen typade felkontrakt bevarad, Stripe-webhooken svarar 503 received:false reason:store_unavailable så retry-vägen hålls öppen. 3 nya tester pinar kontraktet (1062 gröna). Prod container på exakt merge-SHA (df6ab70) health/ready 200. Stänger.
admin closed this issue 2026-09-22 04:09:30 +00:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/0pt#53