Repository navigation
[Bug]: AgentRouter 400 content-blocked still affects openai-chat adapter (AGR-OAI) — apply language framing on Chat/Responses #2415
Description
Summary
#2074 fixed the non-English content-blocked 400 for the anthropic adapter only. The openai-chat adapter (AGR-OAI provider with adapter: "openai-chat") still 400s on non-English first user messages on every wire — /v1/responses, /v1/chat/completions, and even a raw Anthropic-format call to the gateway.
Affected: gpt-5.6-sol (and any OpenAI-format model routed through AgentRouter) used by Codex CLI (wire_api = "responses") and chat-style clients. The model is listed in subagentModels, so subagent turns in PT fail hard.
Reproduction (verified against agentrouter.org with a real key)
All requests use model: "gpt-5.6-sol" (endpoint support: ["openai"] only per /v1/models), base https://agentrouter.org:
| # | Wire | First message | Result |
|---|---|---|---|
| 1 | /v1/chat/completions (via proxy) |
"What is the capital of Brazil?" (EN) |
200 |
| 2 | /v1/chat/completions (via proxy) |
"Qual a capital do Brasil?" (PT) |
400 content-blocked |
| 3 | /v1/responses (via proxy) |
"Qual a capital do Brasil?" (PT) |
400 content-blocked |
| 4 | Anthropic /v1/messages direct with claude-cli headers |
PT, no framing | 400 content-blocked |
| 5 | Anthropic /v1/messages direct with claude-cli headers |
PT + [Instruction: ...] framing |
200 |
| 6 | /v1/chat/completions direct with all claude-cli/X-Stainless headers |
PT, no framing | 400 content-blocked (headers do NOT bypass the filter) |
| 7 | /v1/responses via proxy + local framing (patch) |
PT | 200 |
So: the gateway validates the opening user turn language on all wires, regardless of adapter, endpoint, or client headers. See #1804 for the original correlation research.
Note on the "model is openai-only" fact
GET /v1/models (with recognized User-Agent) reports:
{"id":"gpt-5.6-sol","model":"gpt-5.6-sol","supported_endpoint_types":["openai"]}A raw Anthropic-format call does work for it today (test #4/#5), but that is undocumented/unofficial behavior — the supported surface is OpenAI-compatible. So the fix must live on the openai-chat path, not by re-routing users to the anthropic adapter.
Proposed fix
Extend the framing from #2074 to the openai-chat adapter. Mirror the existing applyAgentRouterLanguageFraming (src/adapters/anthropic.ts) with:
- Host exact match on
agentrouter.org/*.agentrouter.org(never substring — same rationale as fix(anthropic): frame the opening turn so AgentRouter stops blocking non-English #2162's comment aboutnotagentrouter.example). - Idempotent: keyed on the leading block/content being exactly the marker, not
includes()(a user quoting the marker later in their prompt must not suppress the frame, and retries/replays must not stack frames). - Frame as its own text block (or prefix for string content) — the user's original text survives byte-for-byte.
- Hook points:
messagesToChatFormat()result inbuildRequest()for the translated Chat path, andbuildOpenAIChatPassthroughRequest()for/v1/chat/completionspassthrough — both are needed; patching only one leaves the other wire 400ing. - Only the first
userrole message is framed (the gateway inspects the opening turn;system/developerprefixes do not help — verified: asystemEN + user PT request still 400s).
Test matrix
| Case | Expected |
|---|---|
AGR-OAI PT via /v1/responses and /v1/chat/completions |
200, answer in PT |
| AGR-OAI EN (no frame needed) | 200, no double frame |
| AGR-CLA (anthropic) | unchanged (#2074 already handles it) |
| Non-AGR openai-chat providers (e.g. OpenAI, command-code, Z.AI) | byte-for-byte unchanged — no framing |
| Retry/replay of a framed turn | single marker, no stacking |
| Subsequent non-English messages after the first | unchanged (only first user turn framed) |
| String content AND content-part arrays (Claude Code style) | covered |
Alternative considered
Shipping a provider-level flag (e.g. languagePreamble: true) instead of host-derived detection. Rejected for scope: the transform is safe, idempotent, and host-scoped, matching the #2074 decision; a flag adds config surface for no behavioral benefit.
References: #2074 (closed, anthropic-only), #2162 (merged PR), #1804 (correlation research), #1689 (closed by research).
Activity
coderabbitai commentedon Aug 22, 2026
🔗 Related PRs
#2082 - [WRONG BRANCH] fix(adapters): frame AgentRouter first messages to pass the language filter [open]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
github-actions commentedon Aug 22, 2026
Issue closed: insufficient detail
Thanks for taking the time to submit this issue. It was closed automatically because the structured report is missing information needed to evaluate or reproduce it.
Please edit the issue to add:
See the Contributing guide for details.
Once the report passes the automated checks, it will be reopened automatically unless a maintainer has changed its state.
rrmlima commentedon Aug 24, 2026
New evidence: the filter scores non-English volume across the WHOLE conversation
The framing-only-the-first-turn mitigation (both upstream
applyAgentRouterLanguageFramingand any first-turn-only patch) is insufficient for real multi-turn sessions. Empirical evidence from production traffic (Command Code session, 2026-08-22/24), all againstclaude-opus-5via/v1/messages, spaced calls to avoid #1689 burst interference:Conclusions
usageStatus: unreported).Impact for harness users
Any coding-agent session in PT/ES longer than a couple of turns hits
400 content-blockedmid-conversation regardless of adapter (anthropic and openai-chat both affected) and regardless of first-turn framing.Ask
Upstream options worth considering:
Happy to provide request IDs and raw payloads (secrets redacted) if useful.