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

  1. Reset auth.json to a JSON object: {} and retry the operation
  2. Inspect the current auth.json content and fix its shape manually (keep credentials safe)
  3. Delete only if you can re-login/re-provision credentials, then recreate the object shape
  4. 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

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


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)