BigPizzaV3/CodexPlusPlus · error
auth.json 必须是 JSON 对象
Error message
auth.json 必须是 JSON 对象
What it means
When updating auth.json (e.g. to set OPENAI_API_KEY), the existing file content is parsed as JSON and must be an object at the top level. If the parsed value is an array, string, number, or other non-object JSON, the update bails because API-key injection only works on a JSON object.
Solutions
- Reset auth.json to a JSON object: {} and retry the operation
- Inspect the current auth.json content and fix its shape manually (keep credentials safe)
- Delete only if you can re-login/re-provision credentials, then recreate the object shape
- Check which other tool wrote the non-object content and stop it from clobbering auth.json
Example fix
// before (auth.json)
"sk-xxxx"
// after (auth.json)
{
"OPENAI_API_KEY": "sk-xxxx"
} Defensive patterns
Strategy: type-guard
Validate before calling
const parsed = JSON.parse(fs.readFileSync(authPath, 'utf8'));
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('auth.json is not a JSON object; reset it to {} first');
} Type guard
const isJsonObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
Try / catch
try { updateAuthKey(key); } catch (e) { if (String(e).includes('auth.json 必须是 JSON 对象')) { fs.writeFileSync(authPath, '{}\n'); updateAuthKey(key); } else throw e; } Prevention
- Never write raw tokens directly into auth.json — always as an object field
- Back up auth.json before other tools modify it
- Check auth.json shape after any third-party tool touches ~/.codex
When it happens
Trigger: Calling the auth.json write/update path when ~/.codex/auth.json contains valid JSON that is not an object — e.g. the file holds a string, an array, or a bare number/bool/null.
Common situations: auth.json was overwritten by another tool with a non-object payload; a user pasted a token directly into auth.json (making it a JSON string); corrupted/partial file content that happens to still be valid JSON but the wrong shape.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- 官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的…
- Chat Completions 上游 Key 不能为空
- 必须是 JSON 对象
- JSON 解析失败:
- 的模型 值必须是字符串
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/53ee88a86cda188b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/relay_config.rs:2796
let mut auth = if auth_contents.trim().is_empty() {
json!({})
} else {
serde_json::from_str::<Value>(auth_contents).with_context(|| "auth.json JSON 解析失败")?
};
if !auth.is_object() {
auth = json!({});
}
if let Some(auth_object) = auth.as_object_mut() {
if api_key.trim().is_empty() {
auth_object.remove("OPENAI_API_KEY");
} else {
auth_object.insert(
"OPENAI_API_KEY".to_string(),
Value::String(api_key.trim().to_string()),
);
}
} else {
anyhow::bail!("auth.json 必须是 JSON 对象");
}
Ok(serde_json::to_string_pretty(&auth)?)
}
fn set_experimental_bearer_token_in_config(
config_contents: &str,
api_key: &str,
) -> anyhow::Result<String> {
let mut doc = parse_toml_document(config_contents)?;
let session_provider_id = active_provider_id(&doc);
let provider_id = if session_provider_id.as_deref() == Some("openai") {
RELAY_PROVIDER.to_string()
} else {
active_or_default_provider_id(&doc)
};
let provider = ensure_provider_table(&mut doc, &provider_id)?;
if api_key.trim().is_empty() {
provider.remove("experimental_bearer_token");View on GitHub (pinned to b1ed92e5e4)