Repository navigation
[Bug]: AgentRouter openai-chat providers need a Codex client fingerprint; non-English fix only covers anthropic #2717
Description
Client or integration
Codex CLI (also reproducible with a direct HTTP client).
Provider or upstream service
AgentRouter (agentrouter.org)
Version
OpenCodex 2.33.0.
Operating system
Linux server (x86_64).
Provider and model
AgentRouter / claude-opus-5.
OpenCodex version
2.33.0, official npm package; affected adapter files verified byte-for-byte against the published tarball.
Endpoint or capability
/v1/responses translated to an AgentRouter openai-chat provider at https://agentrouter.org/v1; client authentication/fingerprint and non-English prompts.
Summary
AgentRouter openai-chat providers fail with 401 unless a recognized Codex client fingerprint is manually configured, and minimal Portuguese requests still fail with 400 content-blocked because the 2.31 fix covers only the Anthropic adapter.
Current behaviour
Two independent failures remain after the AgentRouter language fix merged in #2162:
- A valid AgentRouter key on a normal
openai-chatprovider is rejected with HTTP 401unauthorized_client_errorunless the operator manually adds an undocumented recognized-client fingerprint (User-Agentororiginator) toprovider.headers. - Once fingerprint authentication succeeds, a minimal Portuguese request still returns HTTP 400
content-blocked. The same provider works from the real Codex CLI because Codex supplies a large English harness context before the Portuguese user prompt.
OpenCodex 2.33.0 implements AgentRouter language framing only in src/adapters/anthropic.ts. src/adapters/openai-chat.ts has no AgentRouter-specific host detection, fingerprint defaults, or language framing.
Controlled header matrix, same key/model/body/process:
| Provider headers | Minimal EN request | Real Codex PT request |
|---|---|---|
| none | 401 unauthorized client | 401 after five retries |
version: 0.149.1 only |
401 | not run |
User-Agent: codex_cli_rs/0.149.1 only |
200 | 200 |
originator: codex_cli_rs only |
200 | 200 |
| UA + originator + version 0.149.1 | 200 | 200 |
| UA + originator + version 0.145.0-alpha.30 | 200 | 200 |
The old version is not required; the recognized Codex identity is what matters.
Expected behaviour
A first-class AgentRouter provider/preset using openai-chat should work with a valid key without requiring operators to discover and copy undocumented magic headers.
OpenCodex should either:
- supply a documented, stable AgentRouter-compatible client fingerprint by default for exact
agentrouter.orghosts; or - expose a documented
clientFingerprintcompatibility option and diagnose the upstream 401 clearly.
AgentRouter's non-English request policy should also be applied consistently to both the Anthropic and OpenAI Chat adapter paths, including translated /v1/responses and raw /v1/chat/completions passthrough.
Operator-specified headers must continue to override defaults, and non-AgentRouter providers must remain unchanged.
Reproduction
The exact minimal reproduction is provided below.
Minimal redacted request or reproduction
# 1. Configure a provider (valid key redacted):
# providers.agentrouter-test = {
# adapter: "openai-chat",
# baseUrl: "https://agentrouter.org/v1",
# authMode: "key",
# apiKey: "<redacted>",
# liveModels: false,
# models: ["claude-opus-5"],
# selectedModels: ["claude-opus-5"],
# defaultModel: "claude-opus-5"
# }
# 2. Send an English request with no custom provider headers:
curl -sS http://127.0.0.1:10100/v1/responses \
-H "Authorization: Bearer $OPENCODEX_API_AUTH_TOKEN" \
-H 'Content-Type: application/json' \
--data '{
"model":"agentrouter-test/claude-opus-5",
"input":"Reply with exactly: OK",
"stream":false,
"store":false,
"max_output_tokens":64
}'
# => HTTP 401 unauthorized client
# 3. Add either header and repeat:
# headers: { "User-Agent": "codex_cli_rs/0.149.1" }
# OR
# headers: { "originator": "codex_cli_rs" }
# => HTTP 200
# 4. With the recognized fingerprint, change input to Portuguese:
# "input":"Responda somente com: OK"
# => minimal direct request: HTTP 400 content-blocked
# 5. Run through the real Codex CLI, which supplies English harness context:
codex exec --ephemeral --skip-git-repo-check \
-m agentrouter-test/claude-opus-5 \
'Responda somente com: OK'
# => HTTP 200 / exact answerActual response or error
# Without recognized client fingerprint:
HTTP 401
{
"error": {
"message": "unauthorized client detected, contact support for assistance"
},
"type": "unauthorized_client_error"
}
# With recognized fingerprint, minimal Portuguese request:
HTTP 400
{
"error": {
"code": "content-blocked",
"type": "agent_router_api_error"
}
}
# With recognized fingerprint, real Codex CLI Portuguese request:
HTTP 200
turn.completedRequest IDs, credentials, account details, and host-specific data were removed.
Upstream documentation
AgentRouter does not appear to publish a public specification documenting the required User-Agent/originator client allowlist or the non-English content-scoring contract. The observable behavior above is based on controlled black-box compatibility testing against the service.
OpenCodex context:
- [Bug]: AgentRouter 400 content-blocked on non-English first messages; add language preamble for AGR providers #2074 — original non-English report
- fix(anthropic): frame the opening turn so AgentRouter stops blocking non-English #2162 — merged Anthropic-only framing fix
- [Bug]: AgentRouter 400 content-blocked still affects openai-chat adapter (AGR-OAI) — apply language framing on Chat/Responses #2415 — OpenAI Chat gap; automatically closed as not planned/insufficient detail, not implemented
- [Bug]: AgentRouter 400 content-blocked under burst traffic; characterize pacing/retry mitigation #1689 — related AgentRouter behavior research
Suggested mapping or implementation notes
- Register AgentRouter as a first-class compatibility policy/preset.
- For parsed hostnames exactly equal to
agentrouter.orgor ending in.agentrouter.org, provide default static headers only when absent:
{
"User-Agent": "codex_cli_rs/<detected-current-version>",
"originator": "codex_cli_rs"
}If impersonating Codex is not acceptable, use a stable OpenCodex-owned fingerprint coordinated/documented with AgentRouter, or require an explicit compatibility option.
- Use existing case-insensitive
mergeRegistryStaticHeaders()semantics so operator headers win. - Share exact-host AgentRouter policy between
anthropicandopenai-chatadapters. - Cover translated
/v1/responsesand raw Chat Completions passthrough. - Preserve user content byte-for-byte, keep framing idempotent, and never match hostile lookalike hostnames such as
agentrouter.org.attacker.example. - Map
unauthorized_client_errorto an actionable diagnostic.
Additional context and attachments
The final local provider was validated across restart with:
- current Codex fingerprint
0.149.1; - two consecutive Portuguese Codex requests returning 200;
- exact expected output;
- no patch in OpenCodex adapters;
- original AgentRouter providers disabled during isolated testing;
- custom provider created through the 2.33.0 management API.
The key conclusion is that #2162 fixed only Anthropic first-turn framing. It did not make AgentRouter openai-chat providers self-configuring, and it did not address AgentRouter's required client identity.
Checks
- I searched existing provider and compatibility issues.
- The request and response were redacted.
- The expected behaviour is based on a concrete Codex CLI client requirement and controlled upstream behavior.
Activity
github-actions commentedon Aug 27, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
rrmlima commentedon Aug 27, 2026
Updated the report to use the repository's recognized issue-template headings and filled every required field requested by the quality bot. The reproduction and all examples are redacted. Please re-evaluate/reopen automatically.
rrmlima commentedon Aug 27, 2026
Follow-up: corrected headings to the exact GitHub issue-form rendering (). For #2717 I also added the literal Bug-template fields , , , , and because the quality bot classified the existing issue as kind=bug. All requested fields are now present.
rrmlima commentedon Aug 27, 2026
Follow-up: corrected headings to the exact GitHub issue-form rendering (
###). For #2717 I also added the literal Bug-template fieldsSummary,Reproduction,Version,Operating system, andProvider and modelbecause the quality bot classified the existing issue askind=bug. All requested fields are now present.lidge-jun commentedon Aug 27, 2026
리뷰 · 우선순위 66 / 80
설명: 이 이슈는 AgentRouter 를 openai-chat 어댑터로 쓸 때 생기는 두 갈래 버그다. 첫째, 인정된 Codex 클라이언트 지문이 없으면 업스트림이 401 unauthorized_client_error 를 낸다. 둘째, 지문을 넣어도 짧은 포르투갈어 요청은 400 content-blocked 가 난다. 작성자는 rrmlima 이다. 라벨은 bug, provider-compatibility, provider 다. 보고 버전은 OpenCodex 2.33.0 이다. 지금 CURRENT
devHEAD 는 ab63ded 이다. package.json 은 아직 2.32.1-preview.20260825 다. 이번 시간 origin/dev 는 움직이지 않았다. 마지막 머지는 2708 문서다. 이 이슈는 열려 있다. 구현 풀은 아직 없다. 내가 머지하지 않고 구현하지 않는다.HEAD 를 보면 본문 주장이 맞다. src/adapters/anthropic.ts 650-698줄에 AGENTROUTER_LANGUAGE_PREAMBLE 과 isAgentRouterEndpoint, applyAgentRouterLanguageFraming 이 있다. 호스트는 agentrouter.org 와 .agentrouter.org 끝만 본다. 938줄에서 anthropic 경로만 그 프레이밍을 켠다. src/adapters/openai-chat.ts 안에는 agentrouter, isAgentRouter, language framing, User-Agent, originator 문자열이 없다. 그래서 openai-chat 커스텀 프로바이더는 기본 지문도 없고 언어 프레이밍도 없다. 본문의 헤더 표와 맞다.
무료 디렉터리 프리셋도 anthropic 쪽이다. src/providers/free-directory.ts 121줄 agentrouter 행은 adapter: anthropic 이다. 운영자가 직접 adapter openai-chat 과 baseUrl https://agentrouter.org/v1 을 만들면 그 프리셋을 타지 않는다. src/providers/registry.ts 에는 agentrouter 정적 행이 없다. mergeRegistryStaticHeaders 는 2844-2856줄에 있고, 사용자 헤더가 같은 이름을 치면 레지스트리 기본을 덮는다. 본문이 말한 운영자 헤더 우선 규칙은 이미 그 함수로 가능하다. 다만 AgentRouter openai-chat 기본 지문을 넣는 호출 자체가 없다.
관련 이슈도 정리된다. 2074 는 비영어 원보고다. 2162 는 anthropic 쪽 언어 프레이밍을 합쳤다. 2415 는 openai-chat 에도 같은 프레이밍을 넣자는 이슈였는데 closed 이고 not planned 쪽이다. 이번 이슈는 2415 의 언어 구멍에다가 클라이언트 지문 401 을 새로 붙였다. 2415 를 다시 열지 말고, 이 이슈 하나로 지문과 언어를 같이 다루는 편이 낫다. types.ts 와 config.ts 가르기와는 무관하다. 프리뷰 배포가 아니다.
라인 650-698 - src/adapters/anthropic.ts 에만 AgentRouter 언어 프레이밍이 있다
라인 938 - anthropic 요청 경로에서만 isAgentRouterEndpoint 일 때 프레이밍을 켠다
경로/심볼 - src/adapters/openai-chat.ts 에 AgentRouter 호스트 검사와 지문 기본값이 없다
라인 121 - src/providers/free-directory.ts agentrouter 프리셋은 adapter anthropic 이다. openai-chat 커스텀은 이 행을 안 탄다
경로/심볼 - src/providers/registry.ts 에 agentrouter 정적 행과 staticHeaders 가 없다
라인 2844-2856 - mergeRegistryStaticHeaders 는 사용자 헤더 우선이다. 기본 지문을 넣어도 운영자 덮어쓰기는 가능하다
이슈 2415 - openai-chat 언어 프레이밍 요청이 이미 닫혀 있다. 이 이슈로 대체해 다루는 편이 맞다
이슈 2162 - anthropic 만 고쳤다. openai-chat 구멍은 남았다
메인테이너의 판단이 필요한 지점
너의 추천
이 이슈는 연다. 닫지 말 것. 구현 풀을 새로 받는다. 정확한 agentrouter.org 호스트에만, 없을 때만 User-Agent 와 originator 기본값을 넣고, anthropic 과 같은 언어 프레이밍을 openai-chat 에도 공유한다. 운영자 headers 는 그대로 이긴다. 적대적 호스트 부분 문자열 매칭은 금지한다. 2415 는 다시 열지 말고 이 이슈에 링크만 남긴다. 라벨은 그대로 둔다. 프리뷰 배포가 아니다. 내가 머지하지 않는다.
이 댓글은 grok-bot이 작성했습니다
rrmlima commentedon Aug 28, 2026
I implemented and validated the focused contribution proposed by the review on clean 2.34.0 main.
Controlled admission matrix, same valid key/model/body:
unauthorized_client_errorversion: 0.149.1onlyUser-Agent: codex_cli_rs/0.149.1onlyoriginator: codex_cli_rsonlyThis proves no stale Codex version needs to be pinned. The smallest stable default is
originator: codex_cli_rs.The prepared change:
notagentrouter.example,agentrouter.org.attacker.example);originator: codex_cli_rsonly to AgentRouteropenai-chatroutes;originatorwin case-insensitively (originatororOriginator), avoiding duplicate equivalent headers;Fingerprint admission and language framing remain separate concerns: a direct minimal Portuguese request can pass 401 admission and still receive 400
content-blocked; the shared framing addresses that second stage.Validation:
The suite includes Anthropic framing regression coverage, OpenAI Chat translated/passthrough paths, header precedence, lookalike hosts, error fidelity, and the full Chat Completions endpoint suite. The implementation is committed locally and ready for a standalone PR. No keys, request IDs, or account data are included.
lidge-jun commentedon Aug 28, 2026
Fixed on
devby commit bb3321c (PR #2843), reimplementing @rrmlima's #2796.The openai-chat adapter path now sends the Codex client identity and request framing, so AgentRouter providers reached over that path get the fingerprint — previously only the
anthropicpath did, which is exactly what this issue reported. The host/framing policy now lives in one shared module so the two adapters cannot diverge again.Verified with
tests/openai-chat-hardening.test.tsdriven RED (61 pass / 3 fail) then GREEN (64 pass / 0 fail), plus an independent security audit of host canonicalization and lookalike-host rejection.Note this PR targeted
dev, so GitHub did not auto-close this issue — closing manually.