Skip to content

fix(routing): let fallback skip a candidate the request cannot fit - #1850

Merged
lidge-jun merged 1 commit into
devfrom
codex/fallback-input-admission-hop
Aug 16, 2026
Merged

fix(routing): let fallback skip a candidate the request cannot fit#1850
lidge-jun merged 1 commit into
devfrom
codex/fallback-input-admission-hop

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Summary

An oversized request was never silently sent — checkInputAdmission (src/server/responses/input-admission.ts:159) already refuses it before any upstream I/O, and it runs on every concrete retry. The defect #1524 describes is what happens next: that local 413 was classified request_too_large, which comboFailureDecision treats as stop, so the first candidate whose context window was too small ended the fallback chain. A larger-window candidate sitting behind it was never tried.

The local preflight refusal now carries its own code, input_admission_refused, and that code is hop-eligible. The distinction is the point:

  • input_admission_refused is our verdict about this candidate, so another candidate may well fit.
  • Upstream context_length_exceeded, and a generic 413 with no structured code, still stop — retrying those elsewhere is guesswork.

The code is read from the structured field, the classified error, and the body text, because the generic classifier maps 413 to its own code and would otherwise swallow the signal on the text path.

Nothing here truncates history or drops images to force a fit; an incompatible candidate is skipped, not squeezed.

Scope note. This is the half of #1524 that is provably wrong today and safe to fix now. The broader "re-evaluate every fallback candidate against fresh request evidence" work needs a model-independent context estimate threaded into PolicyRequestEvidence (which currently leaves request size unknown) and has to reconcile evaluator exact-fit semantics with ADMISSION_TOLERANCE = 2.5; that design is written up in devlog/_plan/260816_wave34_closeout/090_1524_capability_preflight.md and is not in this PR.

Refs #1524.

Verification

  • bun test tests/combos.test.ts — 37 pass / 0 fail, including a new case pinning that input_admission_refused hops while upstream context_length_exceeded and a bare 413 still stop. Driven red with the branch disabled.
  • bun test tests/policy-execution.test.ts tests/routing-profile.test.ts — green.
  • bun x tsc --noEmit — clean.

Checklist

  • Focused regression test added near the existing failover tests
  • Typecheck clean
  • Targets dev
  • No user-facing config surface added

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of requests rejected before processing due to input limits, allowing eligible failover behavior.
    • Distinguished local input-admission refusals from upstream context-length errors.
    • Preserved terminal handling for upstream context-length and generic request-size errors.
    • Maintained the existing HTTP 413 response and explanatory message for locally rejected requests.
  • Tests

    • Added coverage for local refusals, upstream context-limit errors, and generic 413 responses.

An oversized request was never silently sent -- `checkInputAdmission` already
refuses it before any upstream I/O. The defect is what happens next: that local
413 was classified `request_too_large`, which `comboFailureDecision` treats as
`stop`, so the FIRST candidate whose context window was too small ended the
fallback chain. A larger-window candidate sitting behind it was never tried.

The local preflight refusal now carries its own code, `input_admission_refused`,
and that code is hop-eligible. The distinction is the whole point:

- `input_admission_refused` is OUR verdict about THIS candidate, so another
  candidate may well fit.
- upstream `context_length_exceeded`, and a generic 413 with no structured
  code, still stop -- retrying those elsewhere is guesswork.

The code is read from the structured field, the classified error and the body
text, because the generic classifier maps 413 to its own code and would
otherwise swallow the signal on the text path. Driven red with the branch
disabled.

Nothing here truncates history or drops images to force a fit; an incompatible
candidate is skipped, not squeezed.

Refs #1524.
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 16, 2026
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e844a5d3-e77d-4d19-87ed-0df63d699b2e

📥 Commits

Reviewing files that changed from the base of the PR and between 73440d4 and d18781c.

📒 Files selected for processing (3)
  • src/combos/failover.ts
  • src/server/responses/core.ts
  • tests/combos.test.ts

Included review availability: Your plan includes up to 10 reviews per rolling hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The response layer now emits input_admission_refused for local context-window admission failures. Combo failover recognizes this code, classified error, or message text as hop-worthy. Tests preserve terminal handling for upstream context-limit and generic 413 errors.

Changes

Input admission failover

Layer / File(s) Summary
Admission refusal error classification
src/server/responses/core.ts:1972-1979
Local context-window admission failures return HTTP 413 with error code input_admission_refused instead of request_too_large.
Combo failover behavior and regression coverage
src/combos/failover.ts:127-136, tests/combos.test.ts:360-367
comboFailureDecision returns "hop" for structured, classified, or message-based input_admission_refused failures. Upstream context_length_exceeded and generic 413 errors still return "stop".

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to d1878

This localized change lets fallback skip a candidate rejected by local input admission while preserving stop behavior for upstream context errors; the reported tests and typecheck are clean, and no actionable merge-blocking risk remains beyond normal checks.

Possibly related PRs

Suggested reviewers: wibias, ingwannu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main routing change: fallback can skip candidates that cannot fit the request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fallback-input-admission-hop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d18781cca9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return formatErrorResponse(
413,
"request_too_large",
"input_admission_refused",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the admission code in the emitted error

When this preflight rejects an oversized non-compaction request, formatErrorResponse passes the message through classifyError; because the message contains “context window,” the formatter emits code: "context_length_exceeded" rather than input_admission_refused. The combo and policy fallback layers therefore receive the existing stop code and never try the larger candidate. Preserve the explicit local-admission code in the serialized response and add focused coverage using the actual formatter or handler output rather than a hand-written error body.

AGENTS.md reference: src/AGENTS.md:L24-L26

Useful? React with 👍 / 👎.

Comment thread src/combos/failover.ts
Comment on lines +132 to +134
if (options?.code === "input_admission_refused"
|| error.code === "input_admission_refused"
|| message.includes("input_admission_refused")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Honor the structured admission code before message inference

Even if the response is changed to preserve input_admission_refused, the real admission message still contains “context window,” so classifyError infers context_length_exceeded and the earlier stop branch returns before this new structured-code check runs. Consequently, comboFailureDecision(413, <real admission body>, { code: "input_admission_refused" }) still returns stop; check the authoritative structured code before the inferred context error and cover the real admission message in the regression test.

AGENTS.md reference: src/AGENTS.md:L24-L26

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun merged commit 8f7a22f into dev Aug 16, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant