{"record":{"id":"1dd50f6afc2847fa","repo":"Hmbown/CodeWhale","slug":"codex-client","errorCode":null,"errorMessage":"Codex client","messagePattern":"Codex client","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":8288,"sourceCode":"            providers: Some(ProvidersConfig {\n                openai_codex: ProviderConfig {\n                    auth_mode: Some(\"oauth\".to_string()),\n                    external_credentials: Some(\n                        codewhale_config::ExternalCredentialConsentToml::read_only(\n                            codewhale_config::ProviderKind::OpenaiCodex,\n                            codewhale_config::ExternalCredentialSource::CodexCli,\n                            path.clone(),\n                        ),\n                    ),\n                    ..ProviderConfig::default()\n                },\n                ..ProvidersConfig::default()\n            }),\n            ..Config::default()\n        };\n\n        crate::external_credentials::reset_side_effect_trap();\n        let client = CodewhaleClient::new(&config).expect(\"Codex client\");\n        assert_eq!(client.api_key, token_a);\n        assert_eq!(client.codex_account_id.as_deref(), Some(\"account-a\"));\n        assert_eq!(\n            crate::external_credentials::side_effect_trap_counts(),\n            (1, 1),\n            \"bearer and account id must come from one secure open/read\"\n        );\n\n        // An owner rotation after construction cannot splice account B into\n        // the already-resolved bearer snapshot.\n        std::fs::write(\n            &path,\n            serde_json::to_string(&serde_json::json!({\"tokens\": {\"access_token\": crate::test_support::future_test_jwt(\"b\"), \"account_id\": \"account-b\"}})).expect(\"serialize rotated fixture\"),\n        )\n        .expect(\"rotate fixture\");\n        assert_eq!(client.api_key, token_a);\n        assert_eq!(client.codex_account_id.as_deref(), Some(\"account-a\"));\n    }","sourceCodeStart":8270,"sourceCodeEnd":8306,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L8270-L8306","documentation":"A panic from `CodewhaleClient::new(&config).expect(\"Codex client\")` in the credential-snapshot test. Client construction resolves the Codex bearer token and account id from the OPENAI_CODEX_AUTH_FILE fixture (with OPENAI_CODEX_ACCESS_TOKEN / CODEX_ACCESS_TOKEN removed) and validates them; construction fails when credential resolution or required client setup fails — e.g. the auth file content is rejected or a TLS/crypto provider could not be initialized.","triggerScenarios":"`CodewhaleClient::new` returns Err in the test after OPENAI_CODEX_AUTH_FILE points at a fixture auth.json whose access_token fails JWT/base64 validation, whose required fields are missing, or when the rustls crypto provider is not installed.","commonSituations":"Editing the fixture JSON into an invalid shape, test env var leakage between tests (lock_test_env not held), expired/malformed hand-written JWTs, or rustls default provider contention with other tests.","solutions":["Validate the fixture auth.json is exactly {\"tokens\":{\"access_token\":<valid JWT>,\"account_id\":\"...\"}} and the JWT is well-formed base64url with future exp.","Ensure crate::test_support::lock_test_env() guards OPENAI_CODEX_AUTH_FILE / OPENAI_CODEX_ACCESS_TOKEN / CODEX_ACCESS_TOKEN so parallel tests cannot leak values.","Install the crypto provider before constructing clients (rustls::crypto::ring::default_provider().install_default()), as done in client_with_config_secret_sentinels.","Run the single test with --test-threads=1 to rule out cross-test env pollution, then bisect."],"exampleFix":"// before\nlet client = CodewhaleClient::new(&config).expect(\"Codex client\");\n// after (defensively, in test setup)\nlet _ = rustls::crypto::ring::default_provider().install_default();\nlet client = CodewhaleClient::new(&config)\n    .unwrap_or_else(|e| panic!(\"Codex client: {e:?}\"));","handlingStrategy":"validation","validationCode":"// rust: pre-validate the fixture before constructing the client\nlet json: serde_json::Value = serde_json::from_slice(&std::fs::read(&path)?)?;\nassert!(json[\"tokens\"][\"access_token\"].is_string(), \"fixture missing access_token\");\nassert!(json[\"tokens\"][\"account_id\"].is_string(), \"fixture missing account_id\");","typeGuard":null,"tryCatchPattern":"let client = CodewhaleClient::new(&config)\n    .unwrap_or_else(|e| panic!(\"Codex client construction failed: {e:?}\"));","preventionTips":["Generate fixture JWTs with the shared test_support::future_test_jwt helper, never hand-written strings.","Always pair EnvVarGuard set/remove with lock_test_env to prevent cross-test env leakage.","Install the rustls ring provider in test setup before any client construction.","Print the Err payload (debug) instead of bare expect when diagnosing."],"tags":["rust","credentials","test-fixture","client-init"],"backgroundTag":"missing-credentials","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}