Hmbown/CodeWhale · error

`rules` in permissions.toml must be an array

Error message

`rules` in permissions.toml must be an array

What it means

append_permission_rule edits the `rules` item in permissions.toml. When `rules` is stored as a TOML value (inline array) it must be a TOML array so an inline table rule can be pushed; if `as_array_mut()` fails, the item is some other value type (string, integer, table).

Solutions

  1. Edit permissions.toml so `rules` is an array: either `rules = [ { tool = "...", ... } ]` or `[[rules]]` sections
  2. Remove the invalid `rules = <scalar>` line entirely so the library can create the array itself
  3. Re-run load_permissions_snapshot to confirm the file parses, then retry the append

Example fix

// before (permissions.toml)
rules = "deny bash"

// after
[[rules]]
tool = "bash"
permission = "deny"
Defensive patterns

Strategy: validation

Validate before calling

let doc: toml_edit::DocumentMut = raw.parse()?;
match doc.get("rules") {
    None | Some(toml_edit::Item::ArrayOfTables(_)) => Ok(()),
    Some(toml_edit::Item::Value(v)) if v.is_array() => Ok(()),
    _ => Err(anyhow::anyhow!("rules must be an array in permissions.toml")),
}

Type guard

fn is_rules_array(item: Option<&toml_edit::Item>) -> bool {
    matches!(item, None | Some(toml_edit::Item::ArrayOfTables(_))
        | Some(toml_edit::Item::Value(v)) if v.as_array().is_some())
}

Try / catch

match result {
    Err(e) if e.to_string().contains("must be an array") => {
        eprintln!("fix `rules` in permissions.toml: it must be [[rules]] tables or an inline array");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling the permission-rule append API while permissions.toml contains `rules` set to a non-array value, e.g. `rules = "x"` or `rules = true`, or `rules` defined as a plain table rather than an array/inline-array.

Common situations: Hand-editing permissions.toml and setting rules to a scalar; a different tool writing rules as a non-array; copy-pasting an invalid snippet into the config file.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/e8f26a19846d4452. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/lib.rs:6340

    hasher.update(raw.as_bytes());
    let digest = hasher.finalize();
    let mut token = String::with_capacity(24);
    for byte in &digest[..12] {
        use std::fmt::Write as _;
        let _ = write!(&mut token, "{byte:02x}");
    }
    token
}

fn append_permission_rule(item: &mut toml_edit::Item, rule: &ToolAskRule) -> Result<()> {
    match item {
        toml_edit::Item::ArrayOfTables(rules) => {
            rules.push(permission_rule_table(rule));
            Ok(())
        }
        toml_edit::Item::Value(value) => {
            let Some(rules) = value.as_array_mut() else {
                bail!("`rules` in permissions.toml must be an array");
            };
            rules.push(toml_edit::Value::InlineTable(permission_rule_inline_table(
                rule,
            )));
            Ok(())
        }
        _ => bail!("`rules` in permissions.toml must be an array"),
    }
}

fn remove_permission_rule_item(item: &mut toml_edit::Item, index: usize) -> Result<Option<String>> {
    match item {
        toml_edit::Item::ArrayOfTables(rules) => {
            if index >= rules.len() {
                bail!("permission rule index changed before removal");
            }
            let file_header = if index == 0 {
                rules

View on GitHub (pinned to 73e0f67d83)