github/spec-kit · error · IntegrationDescriptorError

Invalid provides.commands: expected a list

Error message

Invalid provides.commands: expected a list

What it means

Raised by IntegrationDescriptor._validate() (src/specify_cli/integrations/catalog.py:773) when parsing an integration.yml descriptor whose 'provides' mapping contains a 'commands' key that is not a YAML list. The library enforces a strict schema (schema_version 1.0) so that catalog tooling can rely on provides.commands being iterable. The check only fires when the key is explicitly present; omitting 'commands' entirely defaults it to an empty list.

Source

Thrown at src/specify_cli/integrations/catalog.py:773

                if not isinstance(tool, dict):
                    raise IntegrationDescriptorError(
                        "Each requires.tools entry must be a mapping"
                    )
                tool_name = tool.get("name")
                if not isinstance(tool_name, str) or not tool_name.strip():
                    raise IntegrationDescriptorError(
                        "requires.tools entry 'name' must be a non-empty string"
                    )

        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'"

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Open the integration.yml cited by the error and make provides.commands a YAML list of mappings.
  2. If the integration provides only scripts, remove the 'commands' key entirely so the default [] applies.
  3. Re-run the descriptor load (or 'specify integration' command) to confirm the error is gone.

Example fix

# before
provides:
  commands: plan

# after
provides:
  commands:
    - name: plan
      file: commands/plan.md
Defensive patterns

Strategy: validation

Validate before calling

import yaml

def validate_provides_commands(path):
    data = yaml.safe_load(path.read_text()) or {}
    provides = data.get("provides", {})
    if "commands" in provides and not isinstance(provides["commands"], list):
        raise ValueError("provides.commands must be a list")
    return provides.get("commands", [])

Type guard

def is_valid_commands_list(commands) -> bool:
    return isinstance(commands, list) and all(
        isinstance(c, dict) for c in commands
    )

Try / catch

from specify_cli.integrations.catalog import IntegrationDescriptor, IntegrationDescriptorError

try:
    desc = IntegrationDescriptor(path)
except IntegrationDescriptorError as e:
    print(f"Bad descriptor {path}: {e}")
    raise SystemExit(1)

Prevention

When it happens

Trigger: Constructing IntegrationDescriptor(descriptor_path) (or any catalog load that instantiates it) on an integration.yml where 'provides:' has 'commands:' mapped to a string, mapping, or scalar instead of a list — e.g. 'commands: plan' or 'commands: {name: plan}'.

Common situations: Hand-authoring an integration.yml for a custom integration and writing a single command as a scalar or a mapping instead of a one-element list; copy-pasting from a JSON-style config where the brackets were dropped; YAML indentation collapsing a list into a mapping.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/8b20d9163aed6034. Report an issue: GitHub.