Skip to content

fix(errors): Codex entitlement/retired-model 400 with a top-level detail body is reported as a model rejection, not context overflow (#81558, #106475, salvage #100783) - #115881

Merged
teknium1 merged 1 commit into
mainfrom
fix/boa-res-R4-routing-catalog-azure-detail-classifier
Sep 19, 2026
Merged

teknium1 merged 1 commit into
mainfrom
fix/boa-res-R4-routing-catalog-azure-detail-classifier

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

A Codex entitlement/retired-model 400 {"detail": "The '<model>' model is not supported when using Codex with a ChatGPT account."} on a large session is now reported as a descriptive request rejection with the provider's own text, instead of being mistaken for context overflow and burning the compression loop.

  • agent/error_classifier.py::_body_message_candidates now yields a top-level detail (string, or nested {"message": ...}) after the existing OpenAI / flat / litellm-Bedrock keys, so every consumer of the body text sees it: the generic-400 "bare body + large session ⇒ overflow" heuristic in _classify_400, _extract_message (the user-visible message), and the Codex masked-replay check.
  • The pydantic list shape of detail is untouched — it is still read only by _oversized_message_content_rejection.
  • Because is_client_error excludes overflow, the existing entitlement marker from fix(agent): unentitled Codex primary + fallback no longer oscillate or announce a false restore (#106475, salvage #106482) #106549 (agent/turn_api_error.pyfallback_cooldown._mark_entitlement_rejected_model) never ran on large sessions; with the body read correctly it now does.
  • One invariant test in tests/agent/test_error_classifier.py (string + nested shapes not overflow, no compression, fallback, message is the provider text; control: a bare Error body on the same session still compresses). Red on base, green on this head.

Live probe (real openai SDK _make_status_error_from_response on an httpx.Response(400) carrying the exact #81558 body → public classify_api_error, provider=openai-codex, 223 messages / ~110k tokens, temp HERMES_HOME):

case base 8df0a037 this PR
entitlement {"detail": …} 400, large session context_overflow, should_compress=True, message Error code: 400 - {'detail': …} format_error, should_compress=False, should_fallback=True, message The 'gpt-5.5-codex' model is not supported when using Codex with a ChatGPT account.
same body, small session (control) format_error format_error (unchanged)
{"detail": {"message": …}}, large session context_overflow format_error
bare {"error": {"message": "Error"}}, large session (heuristic control) context_overflow context_overflow (unchanged)

Root cause: _body_message_candidates never read the FastAPI-style top-level detail key, so a long, descriptive rejection counted as an empty body and tripped the large-session overflow heuristic.

Tests: tests/agent/test_error_classifier.py tests/agent/test_auxiliary_error_classification.py tests/agent/test_entitlement_fail_closed.py → 177 passed.

Refs #81558 (the commenter's "400 + large session ⇒ Context length exceeded … Cannot compress further" loop; the OP's wire-format hypothesis is contradicted in-thread and stays unreproduced)
Refs #106475 (already closed by #106549; this makes that fix reachable on large sessions)
Salvages #100783 (@i-Hun) — slim re-port of its detail-body half onto the current rule-table classifier; co-authored.

Dropped hunks

Infographic

infographic

… a bare 400

`agent/error_classifier.py::_body_message_candidates` never yielded the
FastAPI-style top-level `detail` key (string, or nested `{"message": ...}`),
so the Codex gateway's `{"detail": "The '<model>' model is not supported when
using Codex with a ChatGPT account."}` 400 read as a *bare* 400. On a large
session `_classify_400`'s generic-400 heuristic then classified it as
context_overflow: the loop burned compression attempts ("Context length
exceeded (109,962 tokens). Cannot compress further.") and, because
`is_client_error` excludes overflow, the entitlement marker from #106549 never
ran. Reading `detail` makes it a descriptive rejection (format_error: abort +
fall back, no compression) and surfaces the provider's text as the message
instead of `Error code: 400 - {...}`. The pydantic list shape of `detail` is
still handled by `_oversized_message_content_rejection` and is not yielded.

Slim re-port of #100783's detail-body half onto the rule-table classifier;
the session/weekly usage-limit half is a separate class and was dropped.

Refs #81558
Refs #106475
Co-authored-by: Oleg Nagornyy <nagornyy.o@gmail.com>
@whyyagswhy

Copy link
Copy Markdown
Contributor

Independent verification on the PR head (7c40b50): error-classifier suite 169/169 green on Linux. Reading a top-level detail body turns a bare 400 into a descriptive error. No findings.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openai OpenAI / Codex Responses API labels Sep 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 7c40b50 — fix: read a top-level detail error body so a descriptive 4

all good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/openai OpenAI / Codex Responses API type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants