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

  1. Replace the scalar permission value with a proper [permission] section in the config.
  2. Delete the permission key and re-run the import so the table is created.
  3. 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

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


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