{"record":{"id":"a0bbcc798ec94c17","repo":"xai-org/grok-build","slug":"permission-key-is-not-an-array","errorCode":null,"errorMessage":"permission.{key} is not an array","messagePattern":"permission\\.(.+?) is not an array","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/claude_import.rs","lineNumber":891,"sourceCode":"            RuleAction::Ask => ask_rules.push(formatted),\n        }\n    }\n\n    for (key, new_rules) in [\n        (\"allow\", allow_rules),\n        (\"deny\", deny_rules),\n        (\"ask\", ask_rules),\n    ] {\n        if new_rules.is_empty() {\n            continue;\n        }\n\n        let arr = perm_table\n            .entry(key)\n            .or_insert_with(|| TomlValue::Array(Vec::new()));\n        let existing = arr\n            .as_array_mut()\n            .ok_or_else(|| anyhow::anyhow!(\"permission.{key} is not an array\"))?;\n\n        // Collect existing strings for dedup.\n        let existing_set: std::collections::HashSet<String> = existing\n            .iter()\n            .filter_map(|v| v.as_str().map(|s| s.to_string()))\n            .collect();\n\n        for rule_str in new_rules {\n            if !existing_set.contains(&rule_str) {\n                existing.push(TomlValue::String(rule_str));\n                count += 1;\n            }\n        }\n    }\n\n    Ok(count)\n}\n","sourceCodeStart":873,"sourceCodeEnd":909,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/claude_import.rs#L873-L909","documentation":"merge_permissions found permission.allow (or permission.deny, etc.) present but not as a TOML array, so rule strings cannot be appended/deduped. Only a missing key gets a fresh empty array; an existing non-array value errors out.","triggerScenarios":"apply_items_to_config merges rules while permission.allow (or another action key) is set to a string, boolean, or table instead of an array of strings.","commonSituations":"Hand-edit produced allow = \"bash\" instead of allow = [\"bash\"]; an inline table was placed where the array belongs.","solutions":["Convert the value to an array: allow = [\"bash\", \"read\"].","Remove the malformed key so merge_permissions creates a fresh array and merges rules.","Ensure all rule entries are strings, not tables or scalars."],"exampleFix":"// before (config.toml)\n[permission]\nallow = \"bash\"\n\n// after\n[permission]\nallow = [\"bash\"]","handlingStrategy":"validation","validationCode":"let root: toml::Value = toml::from_str(&std::fs::read_to_string(&path)?)?;\nfor key in [\"allow\", \"deny\"] {\n    if let Some(v) = root.get(\"permission\").and_then(|p| p.get(key)) {\n        if !v.is_array() {\n            anyhow::bail!(\"permission.{key} must be an array of strings\");\n        }\n    }\n}","typeGuard":"fn rule_is_string_array(v: &toml::Value) -> bool {\n    v.as_array().map(|a| a.iter().all(|x| x.is_str())).unwrap_or(false)\n}","tryCatchPattern":"if let Err(e) = apply_import(items, &path) {\n    if e.to_string().contains(\"is not an array\") {\n        eprintln!(\"Wrap rule values in [ ... ], e.g. allow = [\\\"bash\\\"]\");\n    } else {\n        return Err(e.into());\n    }\n}","preventionTips":["Always express permission rules as arrays of strings, even for a single rule.","Validate permission.* values are arrays before importing.","Copy the section shape from generated examples rather than inventing syntax."],"tags":["toml","config","schema"],"backgroundTag":"invalid-toml-config","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}