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
- Rewrite the config so its root is a table of key = value pairs / [sections].
- Move the file aside and let the import create a fresh table-based config.
- 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
- Ensure configs always start with key = value or [section] syntax, never a bare array/scalar.
- Validate root type with tomlq before importing.
- Regenerate the config from a template if its structure is unknown.
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
- [claude_compat] is not a table
- [permission] is not a table
- permission.{key} is not an array
- [mcp_servers] is not a table
- [paths] is not a table
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/0c73a51aff3be37c.
Report an issue: GitHub.