From 4e8e2ce691567ae9baa39e1b18540e410bd098b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=93=88=E5=9F=BA=E7=B1=B3?= <140241684+BlueX888@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:08:27 +0800 Subject: [PATCH] fix(models): ignore a non-string account_id in the Codex auth file (#5601) 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. --- .../deerflow/models/credential_loader.py | 3 ++ backend/tests/test_codex_provider.py | 17 ++++++++ backend/tests/test_credential_loader.py | 42 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/backend/packages/harness/deerflow/models/credential_loader.py b/backend/packages/harness/deerflow/models/credential_loader.py index 05f839ed1..5a623222d 100644 --- a/backend/packages/harness/deerflow/models/credential_loader.py +++ b/backend/packages/harness/deerflow/models/credential_loader.py @@ -241,6 +241,9 @@ def load_codex_cli_credential() -> CodexCliCredential | None: access_token = data.get("access_token") or data.get("token") or tokens.get("access_token", "") account_id = data.get("account_id") or tokens.get("account_id", "") + if not isinstance(account_id, str): + logger.debug("Codex CLI credentials file has a non-string account_id; using no account") + account_id = "" if not access_token: logger.debug("Codex CLI credentials file exists but no token found") return None diff --git a/backend/tests/test_codex_provider.py b/backend/tests/test_codex_provider.py index 6735c7132..2744b407d 100644 --- a/backend/tests/test_codex_provider.py +++ b/backend/tests/test_codex_provider.py @@ -361,3 +361,20 @@ def test_parse_tool_call_arguments_non_dict_json(): parsed, err = model._parse_tool_call_arguments({"arguments": '["list", "not", "dict"]', "name": "t", "call_id": "c"}) assert parsed is None assert err is not None + + +# --------------------------------------------------------------------------- +# Credential loading +# --------------------------------------------------------------------------- + + +def test_model_post_init_accepts_null_account_id(tmp_path, monkeypatch): + auth_path = tmp_path / "auth.json" + auth_path.write_text(json.dumps({"tokens": {"access_token": "tok-test", "account_id": None}})) + monkeypatch.setenv("CODEX_AUTH_PATH", str(auth_path)) + + from deerflow.models.openai_codex_provider import CodexChatModel + + model = CodexChatModel(model="gpt-5.4", reasoning_effort="medium") + + assert model._account_id == "" diff --git a/backend/tests/test_credential_loader.py b/backend/tests/test_credential_loader.py index 2df804f68..e51b177ad 100644 --- a/backend/tests/test_credential_loader.py +++ b/backend/tests/test_credential_loader.py @@ -406,3 +406,45 @@ def test_codex_chat_model_reports_missing_credential_for_non_object_auth_file(tm with pytest.raises(ValueError, match="Codex CLI credential not found"): CodexChatModel(model="gpt-5.4") + + +def test_load_codex_cli_credential_defaults_null_account_id(tmp_path, monkeypatch): + auth_path = tmp_path / "auth.json" + auth_path.write_text( + json.dumps( + { + "tokens": { + "access_token": "codex-access-token", + "account_id": None, + } + } + ) + ) + monkeypatch.setenv("CODEX_AUTH_PATH", str(auth_path)) + + cred = load_codex_cli_credential() + + assert cred is not None + assert cred.access_token == "codex-access-token" + assert cred.account_id == "" + + +def test_load_codex_cli_credential_ignores_non_string_account_id(tmp_path, monkeypatch): + auth_path = tmp_path / "auth.json" + auth_path.write_text( + json.dumps( + { + "tokens": { + "access_token": "codex-access-token", + "account_id": 12345, + } + } + ) + ) + monkeypatch.setenv("CODEX_AUTH_PATH", str(auth_path)) + + cred = load_codex_cli_credential() + + assert cred is not None + assert cred.access_token == "codex-access-token" + assert cred.account_id == ""