Hmbown/CodeWhale · error

failed to parse config at

Error message

failed to parse config at {}; file contents were omitted

What it means

`mutate_config_document` parses the existing config file as a TOML document before applying a mutation. If the file's contents are non-empty but not valid TOML, it aborts rather than risk destroying data. The message intentionally omits the file contents so secrets in the config are never echoed into logs.

Solutions

  1. Run `toml-lint`/`taplo check` or a TOML parser on the file at the path in the message to find the syntax error.
  2. Fix the TOML syntax by hand (common culprits: unclosed strings, duplicate keys, malformed `[table]` headers).
  3. If the file is unrecoverable, restore from backup or version control, then retry the command.
  4. Do not paste the file contents into shared logs/issues — it may contain API keys.

Example fix

// before (broken TOML)
model = "gpt-4o
// after
model = "gpt-4o"
Defensive patterns

Strategy: validation

Validate before calling

// check TOML validity before any config mutation
let raw = std::fs::read_to_string(&path)?;
if !raw.trim().is_empty() {
    raw.parse::<toml_edit::DocumentMut>()
        .map_err(|e| format!("config TOML invalid, fix before running commands: {e}"))?;
}

Try / catch

match cmd.run() {
    Err(e) if e.to_string().contains("failed to parse config") => {
        eprintln!("Fix the TOML file manually; contents not shown for safety.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any config mutation (API key revoke, targeted updates, lock-based writes) when `path` contains non-empty text that `toml_edit::DocumentMut` cannot parse.

Common situations: A manual edit broke TOML syntax (unclosed quote, bad table header), another tool wrote non-TOML content into the config, or a partially completed write left the file truncated mid-statement.

Understand the failure class

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/00cd1376009ef8b6. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/config_document.rs:30

use anyhow::{Context, Result, bail};

use crate::{
    checked_path_exists, normalize_config_file_path, persistence, read_checked_config_file,
    write_one_time_config_backup,
};

/// Parse the latest document under the shared write lock, apply `mutate`, and
/// atomically persist only the resulting delta.
pub fn mutate_config_document<T, F>(path: &Path, mutate: F) -> Result<T>
where
    F: FnOnce(&mut toml_edit::DocumentMut) -> Result<T>,
{
    with_config_write_lock(path, |path| {
        let original = read_optional_config(path)?;
        let mut document = match original.as_deref() {
            Some(raw) if !raw.trim().is_empty() => {
                raw.parse::<toml_edit::DocumentMut>().map_err(|_| {
                    anyhow::anyhow!(
                        "failed to parse config at {}; file contents were omitted",
                        crate::quote_os_path(path)
                    )
                })?
            }
            _ => toml_edit::DocumentMut::new(),
        };
        heal_extras_nesting(&mut document);
        let result = mutate(&mut document)?;
        let body = document.to_string();
        if original.as_deref() == Some(body.as_str()) || (original.is_none() && body.is_empty()) {
            return Ok(result);
        }
        persist_locked(path, original.as_deref(), body.as_bytes())?;
        Ok(result)
    })
}

View on GitHub (pinned to 73e0f67d83)