{"record":{"id":"e8f26a19846d4452","repo":"Hmbown/CodeWhale","slug":"rules-in-permissions-toml-must-be-an-array","errorCode":null,"errorMessage":"`rules` in permissions.toml must be an array","messagePattern":"`rules` in permissions\\.toml must be an array","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/lib.rs","lineNumber":6340,"sourceCode":"    hasher.update(raw.as_bytes());\n    let digest = hasher.finalize();\n    let mut token = String::with_capacity(24);\n    for byte in &digest[..12] {\n        use std::fmt::Write as _;\n        let _ = write!(&mut token, \"{byte:02x}\");\n    }\n    token\n}\n\nfn append_permission_rule(item: &mut toml_edit::Item, rule: &ToolAskRule) -> Result<()> {\n    match item {\n        toml_edit::Item::ArrayOfTables(rules) => {\n            rules.push(permission_rule_table(rule));\n            Ok(())\n        }\n        toml_edit::Item::Value(value) => {\n            let Some(rules) = value.as_array_mut() else {\n                bail!(\"`rules` in permissions.toml must be an array\");\n            };\n            rules.push(toml_edit::Value::InlineTable(permission_rule_inline_table(\n                rule,\n            )));\n            Ok(())\n        }\n        _ => bail!(\"`rules` in permissions.toml must be an array\"),\n    }\n}\n\nfn remove_permission_rule_item(item: &mut toml_edit::Item, index: usize) -> Result<Option<String>> {\n    match item {\n        toml_edit::Item::ArrayOfTables(rules) => {\n            if index >= rules.len() {\n                bail!(\"permission rule index changed before removal\");\n            }\n            let file_header = if index == 0 {\n                rules","sourceCodeStart":6322,"sourceCodeEnd":6358,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/lib.rs#L6322-L6358","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Edit permissions.toml so `rules` is an array: either `rules = [ { tool = \"...\", ... } ]` or `[[rules]]` sections","Remove the invalid `rules = <scalar>` line entirely so the library can create the array itself","Re-run load_permissions_snapshot to confirm the file parses, then retry the append"],"exampleFix":"// before (permissions.toml)\nrules = \"deny bash\"\n\n// after\n[[rules]]\ntool = \"bash\"\npermission = \"deny\"","handlingStrategy":"validation","validationCode":"let doc: toml_edit::DocumentMut = raw.parse()?;\nmatch doc.get(\"rules\") {\n    None | Some(toml_edit::Item::ArrayOfTables(_)) => Ok(()),\n    Some(toml_edit::Item::Value(v)) if v.is_array() => Ok(()),\n    _ => Err(anyhow::anyhow!(\"rules must be an array in permissions.toml\")),\n}","typeGuard":"fn is_rules_array(item: Option<&toml_edit::Item>) -> bool {\n    matches!(item, None | Some(toml_edit::Item::ArrayOfTables(_))\n        | Some(toml_edit::Item::Value(v)) if v.as_array().is_some())\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"must be an array\") => {\n        eprintln!(\"fix `rules` in permissions.toml: it must be [[rules]] tables or an inline array\");\n    }\n    other => other?,\n}","preventionTips":["Never hand-write `rules = <scalar>`; only arrays are valid","Edit permissions.toml through the library API instead of manual rewrites","After manual edits, load a snapshot to validate before further API calls"],"tags":["config","toml","permissions","schema"],"backgroundTag":"config-type-mismatch","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}