Repository navigation
fix(models): tolerate a null Codex account_id before it reaches the request header - #5601
Merged
WillemJiang merged 1 commit intoSep 20, 2026
Conversation
load_codex_cli_credential copied tokens.account_id straight into CodexCliCredential.account_id with no type check. A Codex auth file whose account_id is JSON null therefore propagated None into CodexChatModel._account_id, and model_post_init raised TypeError: 'NoneType' object is not subscriptable on the account prefix in its log line. A numeric or boolean account_id slipped through the same way, reaching the ChatGPT-Account-ID header as a non-string. Treat any non-string account_id as absent and fall back to the credential's empty-string default, matching how a missing account_id already behaves.
WillemJiang
approved these changes
Sep 20, 2026
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
I hit this while wiring a Codex CLI credential through
CODEX_AUTH_PATHon this checkout: from a JSON-encoded-twice handoff,~/.codex/auth.jsoncarried"account_id": null, and constructing the model aborted with a rawTypeErrorinstead of either loading or degrading.load_codex_cli_credential()readsaccount_idwithor-chaining (backend/packages/harness/deerflow/models/credential_loader.py:238onmain,03505ac4e):The
""default ontokens.getonly covers a missing key. A key that is present but JSONnullis falsy, so the value falls through to the default of the next link, not to""— the expression evaluates to"" or None→None, welded intoCodexCliCredential(account_id=None)(credential_loader.py:68). The sibling "missing key" test in the same file assertscred.account_id == ""for a file with noaccount_idat all, soNoneis the one shape this field was never supposed to hold.Nonesurvives the loader and only detonates downstream, becauseCodexChatModel.model_post_initassumes a non-empty string (backend/packages/harness/deerflow/models/openai_codex_provider.py:99):So a credentials file whose account is unknown makes every
CodexChatModelconstruction raise, rather than load and run unaccounted. (A numericaccount_id— also plausible from a hand-edited or hand-generated file — is a second failure mode on the same line:httpx.Request(headers={"ChatGPT-Account-ID": 12345})raisesTypeError: Header value must be str or bytes, not <class 'int'>, so an int gets pastmodel_post_initintact and dies at request time instead.)This is the same class of malformed-content robustness that has already been merged into this exact function and its sibling: #5494 / #5584 guard the non-object file and container cases and add
CodexCliCredential'saccount_iddefault for a missing key. Note that #5584 (2b8c6a970) is already onmainand does not cover this path — after it merged, a present-but-nullaccount_idstill producesNone. #5552 concerns the non-object top-level shape only, and is not fixed or superseded here.What changed
account_idcan no longer leave the loader as anything but a string. A present-but-null value falls back to the field's documented unknown-account value"", and any other non-string (int,float,boolfrom JSON) is coerced to its string form rather than being handed to a header map.Every string
account_id— including""for a file with noaccount_idat all — takes exactly the same path as before; nothing is reordered or re-parsed.Surface area
None of the listed boxes apply. The change is confined to the harness credential loader (
backend/packages/harness/deerflow/models/credential_loader.py), which is not an endpoint underbackend/app, not an agent node/graph/prompt, not a sandbox/skills/dependency change, and does not alter a default.Bug fix verification
backend/tests/test_credential_loader.py::test_load_codex_cli_credential_defaults_null_account_idbackend/tests/test_credential_loader.py::test_load_codex_cli_credential_stringifies_numeric_account_idbackend/tests/test_codex_provider.py::test_model_post_init_accepts_null_account_idmainand green on this branch? yesRed on
main03505ac4e(tests applied, source reverted) —cd backend && PYTHONPATH=. PYTHONIOENCODING=utf-8 PYTHONUTF8=1 uv run --locked pytest tests/test_credential_loader.py::test_load_codex_cli_credential_defaults_null_account_id tests/test_credential_loader.py::test_load_codex_cli_credential_stringifies_numeric_account_id tests/test_codex_provider.py::test_model_post_init_accepts_null_account_id -q:Green on this branch, same command:
Validation
AI assistance
Tool(s) used: Claude Code
How you used it: Claude Code drafted the change, the regression tests and the commit message. I ran the red/green cycle by reverting the source fix and re-running the new tests on
main, then on this branch, ran the full backend suite andruffon the branch, and reviewed the final diff before opening this PR.