xai-org/grok-build · error
[permission] is not a table
Error message
[permission] is not a table
What it means
merge_permissions found a permission key at the config root whose value is not a table, so allow/deny rule arrays cannot be merged under it. The function only creates the table when the key is absent.
Source
Thrown at crates/codegen/xai-grok-shell/src/claude_import.rs:859
}
Ok(count)
}
/// Merge permission rules into `[permission]` using the compact format.
///
/// Existing rules are preserved. New rules are appended to the appropriate
/// action list (`allow`, `deny`, `ask`).
fn merge_permissions(
table: &mut TomlMap<String, TomlValue>,
rules: &[&PermissionRule],
) -> anyhow::Result<usize> {
let permission = table
.entry("permission")
.or_insert_with(|| TomlValue::Table(TomlMap::new()));
let perm_table = permission
.as_table_mut()
.ok_or_else(|| anyhow::anyhow!("[permission] is not a table"))?;
let mut count = 0;
// Group rules by action.
let mut allow_rules: Vec<String> = Vec::new();
let mut deny_rules: Vec<String> = Vec::new();
let mut ask_rules: Vec<String> = Vec::new();
for rule in rules {
let formatted = format_rule_string(rule);
match rule.action {
RuleAction::Allow => allow_rules.push(formatted),
RuleAction::Deny => deny_rules.push(formatted),
RuleAction::Ask => ask_rules.push(formatted),
}
}
for (key, new_rules) in [View on GitHub (pinned to bc7f02eddd)
Solutions
- Replace the scalar permission value with a proper [permission] section in the config.
- Delete the permission key and re-run the import so the table is created.
- Use an inline table: permission = { allow = [], deny = [] }.
Example fix
// before (config.toml) permission = "allow" // after [permission] allow = ["bash"] deny = []
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("permission") {
if !v.is_table() {
anyhow::bail!("permission must be a [permission] table");
}
} Type guard
fn permission_is_table(root: &toml::Value) -> bool {
root.get("permission").map(|v| v.is_table()).unwrap_or(true)
} Try / catch
if let Err(e) = apply_import(items, &path) {
if e.to_string().contains("[permission] is not a table") {
eprintln!("Convert the permission key to a [permission] section, then retry");
} else {
return Err(e.into());
}
} Prevention
- Write permission rules only under a [permission] section.
- Never assign a scalar/string to the permission key.
- Let the importer create the section rather than hand-authoring it.
When it happens
Trigger: apply_items_to_config processes permission rules while the config has a non-table value bound to permission, e.g. permission = "allow-all".
Common situations: User wrote permission as a string/boolean instead of a [permission] section; a different tool claimed the key with a scalar value.
Related errors
- config root is not a table
- [claude_compat] 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/196ea5ba458bc351.
Report an issue: GitHub.