Skip to content

fix(resilience): a request-translation failure no longer cools the account or trips the provider breaker (#14815) - #14830

Merged
diegosouzapw merged 3 commits into
release/v3.8.51from
fix/translation-failure-no-account-cooldown
Sep 25, 2026
Merged

diegosouzapw merged 3 commits into
release/v3.8.51from
fix/translation-failure-no-account-cooldown

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

⚠️ base-red inherited: #14547

Refs #14815

Problem

In #14815 a single large image made request translation throw Maximum call stack size exceeded. chatCore turned that into a bare 500, and chat.ts handled it like an upstream failure:

  • markAccountUnavailable() put the account into cooldown. On a single-account install, every later request, including ones that would translate fine, got all 1 active accounts rate limited (reset after 5s).
  • The 500 also counted toward the provider-wide circuit breaker (500 is in its trip set), so a client repeating the request could open the breaker for every account of the provider.

Translation runs locally on the client's own body, before any upstream call. The failure says nothing about the account, and every account fails the same body the same way, so rotating accounts does not help either.

Fix

  • createTranslationFailureResult() sets the internal label errorCode: "request_translation_failed" (TRANSLATION_FAILURE_CODE) on the result object. The body the client receives is unchanged.
  • shouldSkipConnDisable() (account cooldown) and shouldTripProviderBreakerForResult() (provider breaker) skip that label, the same way they already skip plugin_block and client_disconnected. The error goes straight back to the client.

Not in this PR

The stack overflow itself did not reproduce. I tested on the current tip and on release/v3.8.50, calling translateRequest directly and going through handleChatCore with a claude OAuth connection, using random-noise images up to 9 MB base64, with stream on and off and compression on and off. The reporter's settings must reach a code path these runs did not hit. This PR removes the availability impact; finding the root cause needs the stack trace from #14815.

Validation

  • tests/unit/translation-failure-skips-account-cooldown-14815.test.ts (5 cases): the result carries the label and keeps the client body unchanged, the cooldown gate and the provider-breaker gate skip it, and an ordinary upstream 500 still cools the account and still trips the breaker. With the two predicates restored from the base, the gate cases fail (red→green shown locally).
  • On 192.168.0.113: typecheck:core 0 errors, check:open-sse-typecheck OK, check:cycles OK. The neighbouring breaker/cooldown suites pass: combo-skip-conn-disable-plugin-block-7806, breaker-network-error-guard, circuit-breaker-local-execution, circuit-breaker-abort-provider-trip-7907 and rate-limit-local-error-classification.
  • The check:mutation-test-coverage entries it still reports (flagship-0day-discovery, metered-budget…, expiry-first…, claude-passthrough-empty-response) come from the tip, not from this PR.

…count or trips the provider breaker (#14815)

Translation runs locally on the client's body before any upstream call. When it
threw, chatCore returned a bare 500 that chat.ts handled like an upstream
failure: markAccountUnavailable cooled the account (a single-account install
then answered every request with "all accounts rate limited") and the 500
counted toward the provider-wide breaker. The same body fails identically on
every account, so rotating is pointless as well.

createTranslationFailureResult() now labels the result with the internal
errorCode request_translation_failed (the client body is unchanged);
shouldSkipConnDisable() and shouldTripProviderBreakerForResult() skip it, so
the error goes straight back to the client.

Refs #14815
@diegosouzapw
diegosouzapw merged commit f4afc71 into release/v3.8.51 Sep 25, 2026
16 of 21 checks passed
fouadSalkini added a commit to fouadSalkini/OmniRoute that referenced this pull request Sep 26, 2026
Slice 1/3 copied src/shared/utils/apiKeyPolicy.ts, its test file and
stryker.conf.json from an older snapshot, silently undoing base work that
landed after that snapshot. Re-apply the base versions and keep only this
PR's own additions (self-service settings on apiKeyInfo, the env-key
forwarding default, two new policy tests, one stryker test entry).

Restored:
- apiKeyPolicy.ts: formatResetDurationSuffix, the "Resets in Xh Ym."
  message suffixes and retryAfter on the budget, token-limit and
  request-limit 429 responses (cheaper-inference#14188)
- tests/unit/api-key-policy.test.ts: readErrorBody, the reset-timing
  assertions and the "returns the token-limit reset instant" test (cheaper-inference#14188)
- stryker.conf.json tap.testFiles: quota-reset-timing and
  combo-skipped-reset-timing (cheaper-inference#14188),
  translation-failure-skips-account-cooldown-14815 (cheaper-inference#14830),
  sudo-password-never-reaches-command-stdin (cheaper-inference#14836)
@diegosouzapw
diegosouzapw deleted the fix/translation-failure-no-account-cooldown branch October 18, 2026 23:51
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.

1 participant