Skip to content

fix: allow combo fallback on context overflow 400 errors - #1331

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.6.7from
oyi77:fix/combo-context-overflow-fallback
Apr 16, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.6.7from
oyi77:fix/combo-context-overflow-fallback

Conversation

@oyi77

@oyi77 oyi77 commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes combo routing fallback behavior for HTTP 400 context overflow errors. When a combo routes through models with different context limits, a 400 "input is too long" or "improperly formed request" from one model should trigger fallback to the next model rather than failing immediately.

Closes #1329

Problem

checkFallbackError() in open-sse/services/accountFallback.ts returns shouldFallback: false for all HTTP 400 responses. This means when a combo's first model rejects a request due to context length (e.g., a 128K model getting a request that exceeds its window), the entire combo fails — even if subsequent models in the combo have larger context windows (200K, 1M, etc.).

This commonly manifests during OpenCode compaction cycles where accumulated context grows large and hits the limit of the first model in a combo chain.

Root Cause

Line 863-866 (before this PR):

if (status === HTTP_STATUS.BAD_REQUEST) {
  return { shouldFallback: false, cooldownMs: 0, reason: RateLimitReason.UNKNOWN };
}

The blanket shouldFallback: false for all 400s assumes the same request will always fail identically on any account. This is correct for malformed API requests but incorrect for context overflow — different models have different context limits.

Fix

Adds pattern-based detection for two categories of 400 errors that should trigger fallback:

  1. Context overflow patterns — "input is too long", "too many tokens", "prompt is too long", "context window", "token limit", etc.
  2. Malformed request patterns — "improperly formed request", "invalid message format", "messages must alternate", "empty message/content"

When detected, returns shouldFallback: true with MODEL_CAPACITY reason (zero cooldown — the model's context limit won't change by waiting).

Unmatched 400 errors continue to return shouldFallback: false as before.

Testing

  • Local patch has been running in production for 48+ hours on our instance
  • Confirmed combos now successfully fallback from smaller-context models to larger ones
  • No false positives observed — unrelated 400 errors still fail fast as expected

Changes

  • open-sse/services/accountFallback.ts — Added context overflow and malformed request pattern detection before the blanket 400 rejection in checkFallbackError()

When a combo routes through models with different context limits, a 400
'input is too long' or 'improperly formed request' error from one model
should trigger fallback to the next model rather than failing immediately.

Previously checkFallbackError() returned shouldFallback:false for ALL
HTTP 400 responses, preventing combo models with larger context windows
from being tried.

Adds pattern-based detection for context overflow and malformed request
errors within 400 responses, returning shouldFallback:true with
MODEL_CAPACITY reason so combos can proceed to the next model.

Closes diegosouzapw#1329
@oyi77
oyi77 requested a review from diegosouzapw as a code owner April 16, 2026 13:57

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the checkFallbackError function to allow fallbacks for specific 400 Bad Request errors, such as context overflows or malformed requests, which might succeed on alternative models. Feedback was provided regarding an unused variable and the efficiency of defining regex patterns within the function scope.

Comment thread open-sse/services/accountFallback.ts Outdated
Comment on lines +868 to +870
const lowerError = errorStr.toLowerCase();

// Context overflow: the prompt exceeds the model's maximum context length.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The lowerError variable is unused in this block because the regex patterns below use the case-insensitive flag (/i) and test against errorStr directly. Removing this line avoids redundant string conversion.

Additionally, consider moving CONTEXT_OVERFLOW_PATTERNS and MALFORMED_REQUEST_PATTERNS to the top level of the file (outside the function). Defining them inside the function causes them to be re-allocated on every 400 error, which is inefficient for a function that may be called frequently during high-traffic periods.

Suggested change
const lowerError = errorStr.toLowerCase();
// Context overflow: the prompt exceeds the model's maximum context length.
// Context overflow: the prompt exceeds the model's maximum context length.

- Move CONTEXT_OVERFLOW_PATTERNS and MALFORMED_REQUEST_PATTERNS to
  module level (avoids re-allocation on every 400 in hot path)
- Remove unused lowerError variable (regexes already use /i flag)
@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.6.7 April 16, 2026 14:43
@oyi77

oyi77 commented Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

CI Status Summary

All CI failures on this PR are pre-existing on the main branch (fb21413, run 24508449023). None are caused by the changes in this PR.

Comparison with main

Check main branch This PR Caused by our changes?
Build ✅ ✅ —
Lint ❌ typecheck:noimplicit:core ❌ Same errors No — pre-existing yazl, github.ts, cloudflare-ai.ts etc.
Unit Tests ❌ tests #368, #1546 ❌ Same 2 tests No
E2E 3/4 ❌ memory-settings.spec.ts ❌ Same test No
E2E 4/4 ❌ proxy-registry + skills-marketplace ❌ Same tests No
Integration ❌ timeout 10m ❌ Same timeout No
Security Scans ❌ TruffleHog ❌ Snyk No — infra issue
PR Test Policy N/A (push) ❌ no test files Policy check only

Gemini Review: Addressed ✅

The review comment about lowerError being unused and patterns being inside the function was already addressed in commit ce1a92c — moved CONTEXT_OVERFLOW_PATTERNS and MALFORMED_REQUEST_PATTERNS to module level, removed unused variable.

@diegosouzapw
diegosouzapw merged commit b140181 into diegosouzapw:release/v3.6.7 Apr 16, 2026
2 checks passed
@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @oyi77 for this great contribution! 🎉 The combo fallback fix for context overflow errors has been integrated into the release/v3.6.7 branch and will be part of the next release. We appreciate your effort!

benzntech added a commit to benzntech/OmniRoute that referenced this pull request Apr 18, 2026
When handleSingleModelChat exhausts all accounts for a provider (all
quota-preflight blocked or rate-limited), it returns a 503 without making
an upstream request. The combo routing engine treated this 503 as a
non-fallbackable error and aborted, never trying subsequent models.

Adds isAllAccountsRateLimitedResponse() that detects the
handleNoCredentials signal (503 + JSON + "unavailable" in message)
and allows combo to proceed to the next model instead of returning
immediately. Applied in both handleComboChat and handleRoundRobinCombo.

Patterns defined at module level to avoid re-allocation on every call
(per PR diegosouzapw#1331 code review style).

Closes diegosouzapw#1331 (related pattern)
benzntech added a commit to benzntech/OmniRoute that referenced this pull request Apr 18, 2026
When handleSingleModelChat exhausts all accounts for a provider (all
quota-preflight blocked or rate-limited), it returns a 503 without making
an upstream request. The combo routing engine treated this 503 as a
non-fallbackable error and aborted, never trying subsequent models.

Adds isAllAccountsRateLimitedResponse() that detects the
handleNoCredentials signal (503 + JSON + "unavailable" in message)
and allows combo to proceed to the next model instead of returning
immediately. Applied in both handleComboChat and handleRoundRobinCombo.

Patterns defined at module level to avoid re-allocation on every call
(per PR diegosouzapw#1331 code review style).

Closes diegosouzapw#1331 (related pattern)
@diegosouzapw diegosouzapw mentioned this pull request Apr 22, 2026
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
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.

Bug: Combo routing doesn't fallback on context overflow errors (400 'Input is too long')

2 participants