BigPizzaV3/CodexPlusPlus · critical

config.toml TOML 解析失败:

Error message

config.toml TOML 解析失败:{error}

What it means

The config.toml loader trims a UTF-8 BOM, treats an empty file as an empty document, and otherwise parses with toml_edit::DocumentMut. When the file content is not valid TOML syntax, the parse error is wrapped as "config.toml TOML 解析失败:{error}". This guards all read/apply operations so they fail fast instead of silently working on a partial document.

Solutions

  1. Read the toml_edit error detail after 解析失败: in the message — it gives line/column; fix that exact syntax problem in config.toml.
  2. Run the file through a TOML validator/parser to enumerate all syntax errors before retrying.
  3. Restore config.toml from backup or from codex defaults and re-apply the profile.
  4. Check for leftover git merge-conflict markers and stray characters near the reported line.

Example fix

// before (config.toml)
model = "gpt-5
model_providers = [openai]
// after (config.toml)
model = "gpt-5"
[model_providers.openai]
base_url = "https://api.openai.com/v1"
Defensive patterns

Strategy: try-catch

Validate before calling

fn config_toml_is_valid(path: &str) -> bool {
    std::fs::read_to_string(path)
        .map(|c| c.trim_start_matches('\u{feff}').parse::<toml_edit::DocumentMut>().is_ok())
        .unwrap_or(false)
}

Try / catch

match apply_relay_profile_to_home_with_switch_rules(...) {
    Err(e) if e.to_string().contains("TOML 解析失败") => {
        // restore config.toml from backup, or surface the toml_edit line/column to the user
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any call that reads config.toml (apply_relay_profile_to_home_with_switch_rules, merge/switch flows) when the file contains syntactically invalid TOML: unclosed brackets, duplicate keys, unterminated strings, or wrong indentation for multi-line strings.

Common situations: Manual edits with a non-TOML-aware editor, paste of JSON instead of TOML, copy-paste losing quotes/brackets, merge conflict markers (<<<<<<<) left in the file, or an editor writing a BOM-plus-garbage file.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/684f1b8d75003fc6. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/relay_config.rs:1409

fn is_custom_provider_id(provider: &str) -> bool {
    !provider.is_empty() && !RESERVED_MODEL_PROVIDER_IDS.contains(&provider)
}

fn provider_table_exists(doc: &DocumentMut, provider_id: &str) -> bool {
    doc.get("model_providers")
        .and_then(Item::as_table)
        .and_then(|table| table.get(provider_id))
        .is_some()
}

fn parse_toml_document(contents: &str) -> anyhow::Result<DocumentMut> {
    let contents = contents.trim_start_matches('\u{feff}');
    if contents.trim().is_empty() {
        Ok(DocumentMut::new())
    } else {
        contents
            .parse::<DocumentMut>()
            .map_err(|error| anyhow::anyhow!("config.toml TOML 解析失败:{error}"))
    }
}

fn remove_provider_specific_common_keys(table: &mut dyn TableLike) {
    for key in PROVIDER_SPECIFIC_COMMON_ROOT_KEYS {
        table.remove(key);
    }
    let sensitive_keys: Vec<String> = table
        .iter()
        .map(|(key, _)| key.to_string())
        .filter(|key| is_provider_credential_root_key(key))
        .collect();
    for key in sensitive_keys {
        table.remove(&key);
    }
    table.remove("model_providers");
}

View on GitHub (pinned to b1ed92e5e4)