unicity-aos/aos-ce · error
`capabilities` must be a TOML table.
Error message
`capabilities` must be a TOML table.
What it means
In `check_capabilities`, if a `capabilities` key exists in Capsule.toml but its value is not a TOML table (e.g. an array or scalar string), the linter emits this error and skips the per-key capability checks. The manifest schema requires capabilities to be a `[capabilities]` table whose keys are capability names.
Solutions
- Change the value to a table: `[capabilities]` with per-capability keys.
- Keep only the fields the capsule actually requires and remove extra data.
- Re-run the forge lints to confirm no other capability errors remain.
Example fix
// before capabilities = ["uplink"] // after [capabilities] uplink = ["net:read"]
Defensive patterns
Strategy: validation
Validate before calling
// Rust
if let Some(cap) = root.get("capabilities") {
if !cap.is_table() { return Err("[capabilities] must be a TOML table"); }
} Type guard
fn is_table(v: &toml::Value) -> bool { v.is_table() } Prevention
- Model capabilities as a map, never a list of scopes at the top level.
- Start from the official capsule manifest template.
- Run the forge lint before committing manifest changes.
When it happens
Trigger: Writing `capabilities = ["uplink"]` (array) or `capabilities = "x"` (string) instead of a table; the key is present but `as_table()` returns None.
Common situations: Copying a JSON-style list into the manifest, misunderstanding whether capabilities is a list of scopes versus a map, or converting older manifests where capabilities had a different shape.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Capsule.toml is not valid TOML
- Capability ` ` must be a list.
- Capability ` ` must be a boolean.
- [package].name is missing or empty.
- [package].version is missing.
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/405a12aa52482970.
Report an issue: GitHub.
Appendix: source
Thrown at capsules/capsule-forge/src/checks.rs:86
};
let mut out = Vec::new();
check_package(&root, &mut out);
check_component(&root, &mut out);
check_capabilities(&root, &mut out);
check_env(&root, &mut out);
let (pub_keys, sub_keys) = collect_topics(&root, &mut out);
check_tool_bus(&sub_keys, &pub_keys, &root, &mut out);
check_topic_shapes(&pub_keys, &sub_keys, &mut out);
out
}
fn check_capabilities(root: &Toml, out: &mut Vec<Finding>) {
let Some(capabilities) = root.get("capabilities") else {
return;
};
let Some(capabilities) = capabilities.as_table() else {
out.push(Finding::err(
"`capabilities` must be a TOML table.",
"Use a [capabilities] table with only the fields required by the capsule.",
));
return;
};
const LIST_FIELDS: &[&str] = &[
"net",
"kv",
"fs_read",
"fs_write",
"host_process",
"net_bind",
"net_connect",
"identity",
];
const BOOL_FIELDS: &[&str] = &["uplink", "allow_persistent", "allow_prompt_injection"];
for (key, value) in capabilities {View on GitHub (pinned to f6f22024fb)