{"record":{"id":"ff01ff4e9e82014d","repo":"unicity-aos/aos-ce","slug":"capability-key-must-be-a-list","errorCode":null,"errorMessage":"Capability `{key}` must be a list.","messagePattern":"Capability `(.+?)` must be a list\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"capsules/capsule-forge/src/checks.rs","lineNumber":107,"sourceCode":"        ));\n        return;\n    };\n    const LIST_FIELDS: &[&str] = &[\n        \"net\",\n        \"kv\",\n        \"fs_read\",\n        \"fs_write\",\n        \"host_process\",\n        \"net_bind\",\n        \"net_connect\",\n        \"identity\",\n    ];\n    const BOOL_FIELDS: &[&str] = &[\"uplink\", \"allow_persistent\", \"allow_prompt_injection\"];\n\n    for (key, value) in capabilities {\n        if LIST_FIELDS.contains(&key.as_str()) {\n            if !value.is_array() {\n                out.push(Finding::err(\n                    format!(\"Capability `{key}` must be a list.\"),\n                    format!(\"Use `{key} = [\\\"scope\\\"]`, or omit it when unused.\"),\n                ));\n            }\n        } else if BOOL_FIELDS.contains(&key.as_str()) {\n            if !value.is_bool() {\n                out.push(Finding::err(\n                    format!(\"Capability `{key}` must be a boolean.\"),\n                    format!(\"Use `{key} = true` or omit it (the default is false).\"),\n                ));\n            }\n        } else {\n            out.push(Finding::warn(\n                format!(\"Unknown capability field `{key}`.\"),\n                \"Use only the current fields documented by `forge_guide` topic `capabilities`.\",\n            ));\n        }\n    }","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/capsules/capsule-forge/src/checks.rs#L89-L125","documentation":"`check_capabilities` validates each key in the `[capabilities]` table against known field kinds: list fields (in LIST_FIELDS) must be TOML arrays of scopes. If a key recognized as a list field holds a scalar or table instead of an array, this error is pushed with the offending key name.","triggerScenarios":"Setting a list-type capability field such as a scope list to a string or boolean, e.g. `uplink = true` or `allowed_scopes = \"net:read\"`.","commonSituations":"Confusing a single-value shorthand with the array form, editing an entry from `\"scope\"` to `[\"scope\"]` incorrectly, or merging configs where the array brackets were lost.","solutions":["Rewrite the value as an array: `{key} = [\"scope\"]`.","If the capability is unused, remove the key entirely.","Check which keys are in LIST_FIELDS in checks.rs to know the exact list-typed names."],"exampleFix":"// before\n[capabilities]\nuplink = \"net:read\"\n\n// after\n[capabilities]\nuplink = [\"net:read\"]","handlingStrategy":"validation","validationCode":"// Rust\nif !value.is_array() {\n    return Err(format!(\"capability `{key}` must be a list\"));\n}","typeGuard":"fn is_scope_list(v: &toml::Value) -> bool { v.is_array() }","tryCatchPattern":null,"preventionTips":["Always write scope capabilities as arrays, even for a single scope.","Consult LIST_FIELDS in checks.rs for the definitive list of array-typed keys.","Keep a lint step in pre-commit hooks."],"tags":["toml","type-mismatch","schema-validation","config"],"backgroundTag":"type-mismatch","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}