Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
fix(api/latest-brief): retry Upstash read once on transient timeout (#3924)
* fix(api/latest-brief): retry Upstash read once on transient timeout
WORLDMONITOR-QJ surfaced as DOMException("operation aborted due to
timeout") at api/latest-brief.ts on Upstash GET. Frequency: 4 events /
19 days, including a 2026-05-13 same-minute double-fire across us-west
and eu-central — a real Upstash regional incident rather than per-user
noise. The handler caught the timeout and returned 503; users saw
"service unavailable" panel on the dashboard.
Add a one-retry helper around `readLatestPointer` + `readBriefPreview`
that re-runs the Upstash GET when AbortSignal.timeout fires. First
attempt gets a 6s budget (up from the shared 3s default); retry shortens
to 3s so total worst-case wall time stays under Vercel Edge's ~25s
initial-response cap after the validateBearerToken + getEntitlements
preflight. Sustained outages still time out cleanly and hit the existing
503 fallback path.
Recovery telemetry: every retry attempt fires `captureSilentError`
tagged `step: 'upstash-retry-attempt'` so the rate of
blipped-but-recovered cases stays visible in Sentry — without it,
successful retries would only appear in Vercel logs and we'd lose the
signal that informs whether the budget is sized right.
Tests: append regression suite asserting (1) one retry on
`name === 'TimeoutError'`, (2) no retry on other error shapes, (3)
re-throw when both attempts time out, (4) per-attempt budgets fire in
the documented order (6s → 3s). The helper and constants are exported as
a test seam (matches `executeTool` in api/mcp/dispatch.ts).
Notes:
- Shared `_upstash-json.js` timeout default stays at 3s; longer budget
is opt-in via explicit `timeoutMs` argument so the 13 other Upstash
callers (MCP fan-out, brief share-url, symbol-search, etc.) keep
their fast-fail semantics.
- Duck-types on `err.name === 'TimeoutError'` rather than
`instanceof DOMException` so retry survives cross-realm cases in test
runners where undici's DOMException can differ from globalThis.
- Hoists `session.userId` to a local const so the retry-helper arrow
closures capture `string` rather than `string | undefined` (TS
narrowing doesn't survive into closure capture sites).
* fix(api): retry latest brief reads on abort E
Elie Habib committed
526f916e36dbbbf555c416dda83d7eabcd460d83
Parent: c613a7f
Committed by GitHub <noreply@github.com>
on 5/27/2026, 3:19:21 PM