xai-org/grok-build · error

config root is not a table

Error message

config root is not a table

What it means

write_import_marker parsed the config but its root value is not a TOML table, so a [claude_compat] section cannot be added as a table entry. The library requires the document root to be a table to insert sections.

Source

Thrown at crates/codegen/xai-grok-shell/src/claude_import.rs:629

    // rewrite would otherwise drop unrelated sections ([model], [ui], etc.)
    // and overwrite a hand-edited config that just happens to have a trailing
    // comma. The user can fix the TOML and retry.
    let mut root: TomlValue = match std::fs::read_to_string(config_path) {
        Ok(s) => toml::from_str(&s).map_err(|e| {
            anyhow::anyhow!(
                "refusing to write import marker: existing config at {} is \
                 not valid TOML ({}). Fix the file (or move it aside) and \
                 retry.",
                config_path.display(),
                e
            )
        })?,
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => TomlValue::Table(TomlMap::new()),
        Err(e) => return Err(e.into()),
    };
    let table = root
        .as_table_mut()
        .ok_or_else(|| anyhow::anyhow!("config root is not a table"))?;
    let compat = table
        .entry("claude_compat")
        .or_insert_with(|| TomlValue::Table(TomlMap::new()));
    let compat_table = compat
        .as_table_mut()
        .ok_or_else(|| anyhow::anyhow!("[claude_compat] is not a table"))?;
    compat_table.insert("imported".to_string(), TomlValue::Boolean(true));

    let toml_str = toml::to_string_pretty(&root)?;
    if let Some(parent) = config_path.parent() {
        std::fs::create_dir_all(parent)?;
    }
    let tmp = config_path.with_extension("toml.tmp");
    // Best-effort cleanup of the .tmp file if either write or rename fails so
    // a failed marker write doesn't leave a stale artefact next to the real
    // config (otherwise the next attempt would inherit a half-written file
    // before the rename clobbers it).
    if let Err(e) = std::fs::write(&tmp, &toml_str) {

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Rewrite the config so its root is a table of key = value pairs / [sections].
  2. Move the file aside and let the import create a fresh table-based config.
  3. Validate the root is a table with tomlq before importing.

Example fix

// before (config.toml)
["a", "b"]

// after
[claude_compat]
imported = true
Defensive patterns

Strategy: validation

Validate before calling

let root: toml::Value = toml::from_str(&std::fs::read_to_string(&path)?)?;
if !root.is_table() {
    anyhow::bail!("config root must be a TOML table");
}

Type guard

fn has_table_root(s: &str) -> bool {
    toml::from_str::<toml::Value>(s).map(|v| v.is_table()).unwrap_or(false)
}

Try / catch

match write_import_marker(&path) {
    Err(e) if e.to_string().contains("root is not a table") => {
        std::fs::write(&path, "")?;
        write_import_marker(&path)?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: The config file's top-level TOML value is a non-table (e.g. a bare array or string), then mark_claude_imported/write_import_marker is called.

Common situations: A config file that accidentally contains only a TOML array like ["a", "b"] or a scalar value instead of key/value sections.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/0c73a51aff3be37c. Report an issue: GitHub.