xai-org/grok-build · error
[claude_compat] is not a table
Error message
[claude_compat] is not a table
What it means
write_import_marker found a key named claude_compat in the config whose value is not a table, so an imported marker cannot be inserted into it. The code only creates a table when the key is missing; a non-table existing value triggers this error.
Source
Thrown at crates/codegen/xai-grok-shell/src/claude_import.rs:635
"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) {
let _ = std::fs::remove_file(&tmp);
return Err(e.into());
}
if let Err(e) = std::fs::rename(&tmp, config_path) {
let _ = std::fs::remove_file(&tmp);
return Err(e.into());View on GitHub (pinned to bc7f02eddd)
Solutions
- Edit the config to replace the scalar claude_compat value with a proper [claude_compat] section.
- Remove the offending key entirely and re-run the import so it creates the table.
- Set claude_compat = { imported = false } as an inline table before importing.
Example fix
// before (config.toml) claude_compat = true // 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 let Some(v) = root.get("claude_compat") {
if !v.is_table() {
anyhow::bail!("claude_compat must be a table, got: {:?}", v.type_name());
}
} Type guard
fn claude_compat_is_table(root: &toml::Value) -> bool {
root.get("claude_compat").map(|v| v.is_table()).unwrap_or(true)
} Try / catch
if let Err(e) = write_import_marker(&path) {
if e.to_string().contains("[claude_compat] is not a table") {
eprintln!("Replace scalar claude_compat key with a [claude_compat] section: {e}");
} else {
return Err(e.into());
}
} Prevention
- Always write claude_compat as a [claude_compat] section, never a bare scalar key.
- Document the expected section shape for users editing configs by hand.
- Prefer letting the import tool create the section rather than pre-creating it manually.
When it happens
Trigger: The config contains e.g. claude_compat = true or claude_compat = "x" at the root, then mark_claude_imported/write_import_marker runs.
Common situations: A user manually added claude_compat as a boolean or string instead of a [claude_compat] section; a prior tool wrote the key in scalar form.
Related errors
- config root 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/e9d038af603d592d.
Report an issue: GitHub.