github/spec-kit · error · IntegrationDescriptorError
Integration must provide at least one command or script
Error message
Integration must provide at least one command or script
What it means
Raised by IntegrationDescriptor._validate() (src/specify_cli/integrations/catalog.py:781) when 'provides' contains neither commands nor scripts (both default to empty lists). Every integration descriptor must declare at least one artifact so the catalog can guarantee an integration actually installs something.
Source
Thrown at src/specify_cli/integrations/catalog.py:781
)
provides = self.data["provides"]
if not isinstance(provides, dict):
raise IntegrationDescriptorError(
"'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():View on GitHub (pinned to bf88c9f9a8)
Solutions
- Add at least one entry to provides.commands (a mapping with 'name' and 'file') or provides.scripts (a relative path string).
- If the integration genuinely provides nothing, reconsider whether it should be an integration descriptor at all.
- Reload the descriptor to verify validation passes.
Example fix
# before
provides: {}
# after
provides:
commands:
- name: plan
file: commands/plan.md Defensive patterns
Strategy: validation
Validate before calling
import yaml
def has_provision(data):
provides = data.get("provides") or {}
commands = provides.get("commands") or []
scripts = provides.get("scripts") or []
return bool(commands) or bool(scripts)
assert has_provision(yaml.safe_load(open("integration.yml"))) Type guard
def provides_something(provides: dict) -> bool:
return bool(provides.get("commands")) or bool(provides.get("scripts")) Prevention
- Treat provides.commands or provides.scripts as mandatory content before shipping a descriptor.
- Add a pre-submit check that rejects empty provides blocks.
- Use a descriptor template that already includes one command entry.
When it happens
Trigger: IntegrationDescriptor(path) on a descriptor where 'provides:' is an empty mapping, or where commands/scripts keys are present but set to empty lists — e.g. 'provides: {}' or 'provides: {commands: [], scripts: []}'.
Common situations: Starting a descriptor from a template and not yet filling in the provides section; accidentally commenting out the command list; trimming a descriptor down for testing and removing all entries.
Related errors
- Invalid provides.commands: expected a list
- Invalid provides.scripts: expected a list
- Each command entry must be a mapping
- 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/e27f27b8c22df505.
Report an issue: GitHub.