Hmbown/CodeWhale · error

Failed to parse config file

Error message

Failed to parse config file {}; file contents were omitted

What it means

Thrown by `load_single_config_file` when a config file reads fine but `toml::from_str::<ConfigFile>` cannot parse it. The path is quoted via `quote_os_path` and, per the codebase-wide convention, the file contents and offending keys are omitted from the message because configs may contain secrets.

Solutions

  1. Run the file through a TOML linter/validator and fix the reported syntax error
  2. Search the file for merge-conflict markers and stray characters, then remove them
  3. Restore the file from version control or back it up and let the app regenerate defaults

Example fix

// before
<<<<<<< HEAD
model = "a"
=======
model = "b"
>>>>>>> main
// after
model = "b"
Defensive patterns

Strategy: try-catch

Validate before calling

let contents = std::fs::read_to_string(&path)?;
contents.parse::<toml::Value>().map_err(|e| anyhow!("config file {} TOML invalid: {e}", path.display()))?;

Try / catch

match load_single_config_file(&path) {
    Ok(cfg) => cfg,
    Err(e) if e.to_string().contains("Failed to parse config file") => {
        eprintln!("Check TOML syntax in {} (contents omitted from the error)", path.display());
        Config::default()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `load_single_config_file(path)` on a config file whose TOML syntax or schema is invalid; the distinct 'Failed to read config file' error covers unreadable files, this one covers unparseable content.

Common situations: A merge conflict marker (`<<<<<<<`) left in config.toml; trailing text after a table; a key duplicated within the same table after hand-merging settings; editing on Windows and pasting a smart quote.

Understand the failure class

Related errors


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

Appendix: source

Thrown at crates/tui/src/config.rs:11543

            ),
            modelstudio_coding_plan: merge_provider_config(
                base.modelstudio_coding_plan,
                override_cfg.modelstudio_coding_plan,
            ),
            modelstudio_coding_plan_anthropic: merge_provider_config(
                base.modelstudio_coding_plan_anthropic,
                override_cfg.modelstudio_coding_plan_anthropic,
            ),
            custom: merge_custom_providers(base.custom, override_cfg.custom),
        }),
    }
}

fn load_single_config_file(path: &Path) -> Result<Config> {
    let contents = fs::read_to_string(path)
        .with_context(|| format!("Failed to read config file: {}", path.display()))?;
    let parsed: ConfigFile = toml::from_str(&contents).map_err(|_| {
        anyhow::anyhow!(
            "Failed to parse config file {}; file contents were omitted",
            codewhale_config::quote_os_path(path)
        )
    })?;
    Ok(*parsed.base)
}

/// Build a one-line warning when top-level-only keys are nested under a section
/// Codewhale does not define (`[general]` / `[sandbox]`). TOML silently drops
/// those keys, so e.g. `[general]\nallow_shell = true` never takes effect and
/// the shell tools (`exec_shell`, `task_shell_start`, …) are absent from the
/// catalog with no explanation. Returns `None` when nothing is misplaced.
///
/// This is the exact confusion behind #2589: `allow_shell` and `sandbox_mode`
/// belong at the top of the file, above any `[section]` header.
fn warn_on_misplaced_top_level_keys(raw: &str) -> Option<String> {
    let doc = toml::from_str::<toml::Value>(raw).ok()?;
    // Sections Codewhale does not recognize but users nest settings under.

View on GitHub (pinned to 73e0f67d83)