Hmbown/CodeWhale · error

serialized typed config was not valid TOML

Error message

serialized typed config was not valid TOML

What it means

config_document round-trips the typed ConfigToml through TOML text (to avoid double-encoding datetime values via Value::try_from) and then parses it back to a toml::Value. If that reparse fails, the serializer produced text that is not valid TOML — an internal invariant violation, since toml::to_string output should always reparse. The error deliberately carries no config contents.

Solutions

  1. Simplify the config: remove unusual/unknown keys or exotic value types from the config file and retry.
  2. Check for a toml crate version mismatch or known round-trip bug; update the dependency.
  3. Report the offending config shape as a bug — this path should never fail for valid typed config.
  4. Validate the config file parses as TOML on its own before exporting/importing.

Example fix

// before (config.toml)
[weird]
key = 1979-05-27T07:32:00Z-extra
// after (config.toml)
[weird]
key = 1979-05-27T07:32:00Z
Defensive patterns

Strategy: try-catch

Validate before calling

let raw = std::fs::read_to_string(config_path)?; let _probe: toml::Value = toml::from_str(&raw).expect("base config must be valid TOML");

Try / catch

match export_bundle(...) { Err(e) if e.to_string().contains("was not valid TOML") => eprintln!("internal round-trip bug; simplify config and report"), other => other, }

Prevention

When it happens

Trigger: toml::from_str on the output of toml::to_string(config) fails inside config_document, which is invoked by export_bundle, prepare_import, and config value inspection helpers.

Common situations: A ConfigToml containing exotic flattened toml::Value extras or unusual value types that the serializer renders in a form the parser rejects — practically a bug in the interaction between the typed config and the toml crate version in use.

Related errors


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

Appendix: source

Thrown at crates/cli/src/config_bundles.rs:966

    if key.starts_with("skills") || key.starts_with("tools") || key.starts_with("snapshots") {
        return ExportSection::Preferences;
    }
    if key.starts_with("auth.") {
        return ExportSection::Drop;
    }
    match scope {
        BundleScope::Global => ExportSection::Global,
        BundleScope::Project => ExportSection::Project,
    }
}

fn config_document(config: &ConfigToml) -> Result<toml::map::Map<String, toml::Value>> {
    // Serialize through TOML text before parsing to Value. Direct
    // `Value::try_from` double-encodes datetime values held inside flattened
    // `toml::Value` extras as the serializer's private marker table.
    let text = toml::to_string(config).context("serializing typed config for bundle")?;
    let value: toml::Value =
        toml::from_str(&text).map_err(|_| anyhow!("serialized typed config was not valid TOML"))?;
    let toml::Value::Table(mut table) = value else {
        bail!("typed config did not serialize to a TOML table");
    };
    // `selected_provider_id` is runtime parse state and is skipped by serde;
    // restore the exact named-provider identity that ConfigStore writes.
    table.insert(
        "provider".to_string(),
        toml::Value::String(config.provider_id().to_string()),
    );
    Ok(table)
}

/// Return a recursively scrubbed export value. Secret-bearing leaves and
/// machine-local paths are omitted rather than replaced with a placeholder,
/// because a placeholder would become literal config on re-import.
fn sanitize_export_value(path: &str, value: &toml::Value) -> Option<toml::Value> {
    sanitize_export_value_at(path, value, 0)
}

View on GitHub (pinned to 73e0f67d83)