Repository navigation
Description
Client or integration
Codex App (desktop, macOS), Codex CLI 0.166.0-alpha.5.1, installed @bitkyc08/opencodex 2.95.0 with the built-in OpenAI provider pointed at the local proxy (127.0.0.1:10100).
Area
Proxy and routing, specifically the account-pool path that forwards Codex turn metadata.
Summary
Every turn in one project failed after the proxy rejected the request with a vague capacity message, so the cause was invisible from the client: the workspace path contained CJK characters, the path was embedded verbatim in x-codex-turn-metadata, and Headers.set refused it.
- In
/Users/<user>/Documents/示例目录/example-project(a git working tree whose absolute path contains示例目录), every single turn failed. Codex retried 5/5, then showedWe’re currently experiencing high demand, which may cause temporary errors.Four separate threads were opened over 25 minutes and all four failed the same way. - In a projectless Codex chat whose directory is pure ASCII (
/Users/<user>/Documents/ascii-chat), the same model answered normally.
The workspace path is only attached to request_kind: "turn" requests, so prewarm succeeded and the client looked healthy right up to the first real question. The failure is therefore not upstream capacity, and the retry loop sends the identical invalid header five times before giving up.
Grepping ~/.codex/logs_2.sqlite for the four failed threads shows the cwd is the CJK directory in all four, and the rendered client error is exactly the capacity string above, which is why this took a long time to attribute.
Reproduction
- Put a git repository under a directory whose absolute path contains non-ASCII characters, for example
/Users/<user>/Documents/示例目录/example-project(git statusclean or dirty, either way). - Configure Codex to use the proxy, then open that directory as the workspace and send any message, e.g.
codex exec "只回复两个字:你好". - Observe that the turn never produces output. The client retries and ends with:
stream disconnected - retrying sampling request (5/5 in 3.34s)... sampling_error=We’re currently experiencing high demand, which may cause temporary errors.
ERROR: We’re currently experiencing high demand, which may cause temporary errors.
- The proxy log (
~/.opencodex/service.log) shows the actual cause, once per attempt, with the source line atsrc/codex/forward-transport-headers.ts:65:
60 | if (installId && installId.trim()) {
61 | headers.set(CODEX_INSTALLATION_ID_HEADER, deriveCodexAccountInstallationId(chatgptAccountId, installId));
62 | }
63 | const turnMeta = headers.get(CODEX_TURN_METADATA_HEADER);
64 | if (turnMeta) {
65 | headers.set(CODEX_TURN_METADATA_HEADER, rewriteTurnMetadataInstallationId(turnMeta, chatgptAccountId));
^
TypeError: Header 'x-codex-turn-metadata' has invalid value
POST - http://127.0.0.1:10100/v1/responses failed
- Repeat the same prompt from a directory with an ASCII-only path; it succeeds on the first attempt with no retries.
Capturing the real outbound bytes on a local listener shows the header the client sends for a CJK workspace (path abbreviated):
{"installation_id":"…","request_kind":"turn","workspaces":{"/Users/<user>/Documents/示例目录/example-project":{"latest_git_commit_hash":"…","has_changes":true}},"model":"a6-ds/deepseek-v4.1-flash-1017"}A minimal, dependency-free reproducer of the failing line — rewriteTurnMetadataInstallationId re-serialises the parsed record, so the CJK path is decoded back to literal characters, and the subsequent Headers.set rejects any code unit above U+00FF:
const meta = JSON.stringify({ installation_id: "abc", workspaces: { "/tmp/中文/项目": {} } });
const rewritten = JSON.stringify({ ...JSON.parse(meta), installation_id: "xyz" });
new Headers({ "x-codex-turn-metadata": rewritten }); // TypeError: Header 'x-codex-turn-metadata' has invalid valueExpected behavior
A workspace path containing CJK characters must work exactly like an ASCII path: the turn reaches the provider, and the metadata round-trips with its values intact. Two supporting expectations:
- Header serialisation should be ASCII-safe, which is what PR Fix non-ASCII Codex turn metadata headers #8040 already does for the parsed-record path.
- If a header still cannot be serialised, the failure should surface as an actionable error naming the header and the offending value rather than being reported to the client as upstream capacity, since that message invites five pointless retries and sends users looking at their provider quota.
Version
@bitkyc08/opencodex 2.95.0. Client: Codex App / Codex CLI 0.166.0-alpha.5.1.
Operating system
macOS 26.7.1 (build 25G241), Apple silicon, bundled Bun 1.4.4.
Logs or error output
Proxy log, repeating for every attempt (~/.opencodex/service.log):
TypeError: Header 'x-codex-turn-metadata' has invalid value
at isolateCodexPoolAccountHeaders (<npm-prefix>/lib/node_modules/@bitkyc08/opencodex/src/codex/forward-transport-headers.ts:65:13)
at materializeCodexUpstreamAuth (<npm-prefix>/lib/node_modules/@bitkyc08/opencodex/src/codex/auth-context.ts:1850:5)
at resolveFirstUsableOpenAiSidecar (<npm-prefix>/lib/node_modules/@bitkyc08/opencodex/src/providers/openai-sidecar.ts:253:25)
at async prepareResponsesSidecarAuth (<npm-prefix>/lib/node_modules/@bitkyc08/opencodex/src/server/responses/request-sidecar-auth.ts:94:29)
at async <anonymous> (<npm-prefix>/lib/node_modules/@bitkyc08/opencodex/src/server/responses/core-pipeline.ts:56:34)
at async handleResponsesInner (<npm-prefix>/lib/node_modules/@bitkyc08/opencodex/src/server/responses/core-pipeline.ts:126:7)
POST - http://127.0.0.1:10100/v1/responses failed
Count of that header error in my log after one afternoon of use: 483, all from the one CJK project.
Client side, the turn status recorded for the four failed threads is failed with We’re currently experiencing high demand, which may cause temporary errors. and 27s, 9s, 23s, 27s durations respectively — i.e. the full retry budget each time.
Screenshots and supporting files
No screenshots; the defect is fully described by the request path, the proxy stack trace, and the captured header above. I can attach a redacted proxy log excerpt if that helps.
Redacted configuration
Default single-account pool on the local machine, no custom routing:
{
"model": "a6-ds/deepseek-v4.1-flash-1017",
"openai_base_url": "http://127.0.0.1:10100/v1",
"codexAccounts": [{ "id": "chatgpt-…", "plan": "plus", "isMain": false }],
"activeCodexAccountId": "chatgpt-…",
"pausedCodexAccountIds": ["__main__"]
}Additional context
A local workaround on 2.95.0 that has held up over repeated real turns: escape every code unit above U+00FF to \uXXXX immediately before writing the header. For a CJK path this reproduces the reported symptom before the change (TypeError: Header 'x-codex-turn-metadata' has invalid value) and passes after it, with the path string comparing equal after JSON.parse:
function toHeaderSafeJson(value: string): string {
let out = "";
for (const ch of value) {
const code = ch.codePointAt(0)!;
if (code <= 0xff) { out += ch; continue; }
const units = code <= 0xffff ? [code] : [0xd800 + ((code - 0x10000) >> 10), 0xdc00 + ((code - 0x10000) & 0x3ff)];
for (const unit of units) out += String.fromCharCode(92) + "u" + unit.toString(16).padStart(4, "0");
}
return out;
}Two notes for whoever picks this up:
- This is the same root cause as PR Fix non-ASCII Codex turn metadata headers #8040, which is still a draft and whose regression test covers Korean and emoji via
JSON.parse/re-serialise. A CJK workspace path exercises the same branch, so the existing fix likely resolves this report too; I am filing it because the searchable symptom here (the capacity message) is what users actually see, and because Compacted Codex Desktop conversation fails with invalid_encrypted_content after service/account changes #8042 references the PR but no issue documented the CJK case. - Worth keeping in mind for coverage: the client-side error mapping is the reason this is expensive to diagnose. Turning an unrepresentable header into a capacity message is indistinguishable from real throttling.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
coderabbitai commentedon Oct 31, 2026
🔗 Related PRs
#8040 - Fix non-ASCII Codex turn metadata headers [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!
zhlf2008 commentedon Nov 1, 2026
Confirming on 2.96.0 (latest, Windows) — still reproducible, and since 2.96 the same crash also takes down non-OpenAI providers.
Environment: Windows 11 ·
@bitkyc08/[email protected](bundled Bun 1.4.4) · Codex app/CLI0.166.0-alpha.5.1· workspaceE:\工作文件\编程\督察管理系统\yangming-supervision(CJK path).Client symptom: identical — Codex retries 5/5, then
We're currently experiencing high demand, which may cause temporary errors.Server side (
~/.opencodex/service.log):New since 2.96: the call now happens under
resolveFirstUsableOpenAiSidecar, which is prepared for every Responses request, so the failure is no longer limited to pooled OpenAI turns. In the same windowgemini-3.8-flash(providergoogle-antigravity) failed every turn with502 upstream_server_error / headers-onlyin 128–176 ms — no request ever reached Google. With the header value made ASCII-safe, the identical requests return 200.Minimal repro (no Codex needed; the header value itself is pure ASCII — the non-ASCII arrives through
\uXXXXescapes, which is what a Rust client emits):\uXXXXfield →200500in ~20 ms,forward-transport-headers.ts:52throwsCause:
rewriteTurnMetadataInstallationId()re-serializes the parsed object withJSON.stringify(), which turns the\uXXXXescapes into literal UTF-8;Headers.set()rejects anything above 0x7F.Fix direction (matches #8040 / #8060): escape non-ASCII on the way out, plus keep the defensive drop:
With that applied locally,
gpt-6-luna,gpt-6.1-solandgemini-3.8-flashall return 200 again — including requests whose metadata contains CJK — so the escaping path is sufficient in practice; the try/catch from #8060 covers anything still unparseable.github-actions commentedon Nov 1, 2026
Automated translation bookkeeping — detected language: English.
Vladimir321123 commentedon Nov 2, 2026
Same bug with Cyrillic paths, so it affects any non-ASCII workspace path, not only CJK. Maybe worth widening the title to "non-ASCII".
Environment: Windows 11,
@bitkyc08/opencodex2.94.0, now 2.96.0 (bundled Bun), Codex CLI 0.164.0, workspace underC:\Users\<user>\Мой диск\....Symptom: identical. Codex retries 5/5 and ends with the "high demand" message, while
service.logshowsTypeError: Header 'x-codex-turn-metadata' has invalid valuethrown fromisolateCodexPoolAccountHeaders(forward-transport-headers.ts), called viaresolveFirstUsableOpenAiSidecarandprepareResponsesSidecarAuth. My log has 240 of these.Still present in 2.96.0:
rewriteTurnMetadataInstallationId()still ends withreturn JSON.stringify({ ...record, installation_id: nextId });. Checked against the pristine 2.96.0 tarball: a turn-metadata header whosecwdcontains escaped Cyrillic makesisolateCodexPoolAccountHeaders(headers, "acct")throw.Local workaround: escaping the re-serialized JSON back to ASCII in that one return statement fixes it. I have been running this since 2.94.0 without side effects:
The catch is that the edit lives inside the global npm package, so every
ocx system update runsilently removes it and the failures come back until it is re-applied. It would be great to get #8040 or #8060 merged.lidge-jun commentedon Nov 3, 2026
Fixed on
devby #8040 (7b3bb7a): after the installation_id rewrite, every code unit outside printable ASCII inx-codex-turn-metadatais escaped, so a workspace path with CJK characters no longer makes the header invalid, and the metadata still decodes to the same values. It ships in the next release; please reopen if it still reproduces after updating.