github/spec-kit · error · IntegrationDescriptorError
Each command entry must be a mapping
Error message
Each command entry must be a mapping
What it means
Raised by IntegrationDescriptor._validate() (src/specify_cli/integrations/catalog.py:786) while iterating provides.commands: an element of the commands list is not a YAML mapping. Each command entry must be a dict (later checked for 'name' and 'file' keys), so scalar or nested-list entries are rejected.
Source
Thrown at src/specify_cli/integrations/catalog.py:786
"'provides' must be a mapping"
)
commands = provides.get("commands", [])
scripts = provides.get("scripts", [])
if "commands" in provides and not isinstance(commands, list):
raise IntegrationDescriptorError(
"Invalid provides.commands: expected a list"
)
if "scripts" in provides and not isinstance(scripts, list):
raise IntegrationDescriptorError(
"Invalid provides.scripts: expected a list"
)
if not commands and not scripts:
raise IntegrationDescriptorError(
"Integration must provide at least one command or script"
)
for cmd in commands:
if not isinstance(cmd, dict):
raise IntegrationDescriptorError(
"Each command entry must be a mapping"
)
if "name" not in cmd or "file" not in cmd:
raise IntegrationDescriptorError(
"Command entry missing 'name' or 'file'"
)
cmd_name = cmd["name"]
cmd_file = cmd["file"]
if not isinstance(cmd_name, str) or not cmd_name.strip():
raise IntegrationDescriptorError(
"Command entry 'name' must be a non-empty string"
)
if not isinstance(cmd_file, str) or not cmd_file.strip():
raise IntegrationDescriptorError(
"Command entry 'file' must be a non-empty string"
)
if os.path.isabs(cmd_file) or ".." in Path(cmd_file).parts or Path(cmd_file).drive or Path(cmd_file).anchor:
raise IntegrationDescriptorError(View on GitHub (pinned to bf88c9f9a8)
Solutions
- Wrap every element of provides.commands in a mapping with at least 'name' and 'file' keys.
- Check for mixed entries (some dicts, some strings) — all must be mappings.
- Re-validate the descriptor.
Example fix
# before
provides:
commands:
- plan
- build
# after
provides:
commands:
- name: plan
file: commands/plan.md
- name: build
file: commands/build.md Defensive patterns
Strategy: validation
Validate before calling
def valid_command_entries(commands):
return all(isinstance(c, dict) for c in commands) Type guard
from typing import Any
def is_command_entry(entry: Any) -> bool:
return isinstance(entry, dict) Prevention
- Each list item under commands must be a mapping with name/file keys.
- Never mix bare strings into the commands list.
- Copy an existing validated entry as the shape reference.
When it happens
Trigger: A descriptor with 'provides: commands: [plan, build]' or a list of lists; the for-loop over commands hits a non-dict element and raises IntegrationDescriptorError immediately.
Common situations: Writing a shorthand list of command names instead of full mappings; converting a JSON list of strings into YAML without wrapping each in an object; inconsistent entry formats within one list.
Related errors
- Invalid provides.commands: expected a list
- Invalid provides.scripts: expected a list
- Integration must provide at least one command or script
- Command entry missing 'name' or 'file'
- Command entry 'name' must be a non-empty string
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/fd768a5003b30a78.
Report an issue: GitHub.