fix(loop): friendly DeepSeek 5xx error with reachability probe#440
Merged
Conversation
Raw `DeepSeek 503: <body>` was bubbling straight to the UI. Users on flaky DS days couldn't tell if it was DS-side (their service was overloaded) or Reasonix-side (we crashed). The header comment in loop/errors.ts even claimed retry.ts swallowed all 5xx — but retry.ts returns the last response instead of swallowing, so 5xx did reach the formatter and just fell through to a passthrough branch. Now formatLoopError takes an optional probe result. The catch in step() detects 5xx, fires a 1.5s `/user/balance` probe, and passes the result back. Three message variants: - no probe → DS-side outage notice + retry hints - probe reachable → "main API answered, /chat/completions failing" - probe down → "DS unreachable from your network — check net" All three include "this is a DeepSeek-side problem, not Reasonix" and a status-page link so users know where to look.
This was referenced May 8, 2026
Use toContain('status.deepseek.com') instead of /status\.deepseek\.com/
— same assertion semantics, dodges CodeQL's high-severity false positive
on unanchored URL regex.
4 tasks
esengine
added a commit
that referenced
this pull request
May 8, 2026
formatLoopError, reasonPrefixFor, and errorLabelFor were all hardcoded English. A Chinese user hitting a 503 / 401 / context overflow saw raw English (and on top of that, the new 5xx outage notice from #440 was also English). Move the strings to a new errors.* i18n namespace covering: - context overflow (with V4/legacy limit mention) - 401 auth / 402 balance / 422 param / 400 bad request - 5xx head + reachable / unreachable / two action variants - reason prefix and label for budget/aborted/context-guard/stuck - "(no message)" fallback for empty error bodies zh-CN translations included. Existing tests still cover EN (vitest setupFile pins runtime to EN); two new tests flip to zh-CN to confirm runtime switch actually translates. Stacked on #440 — merge that first.
This was referenced May 8, 2026
Merged
ChasLui
pushed a commit
to ChasLui/DeepSeek-Reasonix
that referenced
this pull request
May 23, 2026
…ine#440) * fix(loop): friendly DeepSeek 5xx error with reachability probe Raw `DeepSeek 503: <body>` was bubbling straight to the UI. Users on flaky DS days couldn't tell if it was DS-side (their service was overloaded) or Reasonix-side (we crashed). The header comment in loop/errors.ts even claimed retry.ts swallowed all 5xx — but retry.ts returns the last response instead of swallowing, so 5xx did reach the formatter and just fell through to a passthrough branch. Now formatLoopError takes an optional probe result. The catch in step() detects 5xx, fires a 1.5s `/user/balance` probe, and passes the result back. Three message variants: - no probe → DS-side outage notice + retry hints - probe reachable → "main API answered, /chat/completions failing" - probe down → "DS unreachable from your network — check net" All three include "this is a DeepSeek-side problem, not Reasonix" and a status-page link so users know where to look. * test: avoid CodeQL js/regex/missing-regexp-anchor on URL substring match Use toContain('status.deepseek.com') instead of /status\.deepseek\.com/ — same assertion semantics, dodges CodeQL's high-severity false positive on unanchored URL regex.
ChasLui
pushed a commit
to ChasLui/DeepSeek-Reasonix
that referenced
this pull request
May 23, 2026
formatLoopError, reasonPrefixFor, and errorLabelFor were all hardcoded English. A Chinese user hitting a 503 / 401 / context overflow saw raw English (and on top of that, the new 5xx outage notice from esengine#440 was also English). Move the strings to a new errors.* i18n namespace covering: - context overflow (with V4/legacy limit mention) - 401 auth / 402 balance / 422 param / 400 bad request - 5xx head + reachable / unreachable / two action variants - reason prefix and label for budget/aborted/context-guard/stuck - "(no message)" fallback for empty error bodies zh-CN translations included. Existing tests still cover EN (vitest setupFile pins runtime to EN); two new tests flip to zh-CN to confirm runtime switch actually translates. Stacked on esengine#440 — merge that first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Raw
DeepSeek 503: <body>was bubbling straight to the UI on every DS-side outage. Users couldn't tell if Reasonix had crashed or DeepSeek's service was struggling — and DS 5xx is common in evening/Asia hours, so this hit real users (red-apple feedback this week).The file header in
src/loop/errors.tseven lied about it:/** Single text-layer DeepSeek-error formatter — 429/5xx never reach here (retry.ts swallows). */But
retry.ts:50returns the last response when retries are exhausted — it doesn't swallow. So 5xx did reachformatLoopErrorand just fell through to the passthrough branch with no friendly message.What
formatLoopError(err, probe?)now takes an optional reachability probe. The catch handler inloop.ts:step()detects 5xx viais5xxError(), fires a 1.5s/user/balanceprobe, and passes the result through. Three message shapes:All three name DeepSeek explicitly and link
https://status.deepseek.comso users know where to look.The lying header comment is gone.
Test plan
tests/loop-error.test.ts(was 22) — three probe states × three 5xx codes + 503 with/without probetsc --noEmitclean