github/spec-kit · error · BundlerError

provides.{kind} must be a list when present.

Error message

provides.{kind} must be a list when present.

What it means

Raised by the manifest helper _parse_refs when a 'provides.<kind>' entry (e.g. provides.skills) is present but is not a YAML list. Each kind under 'provides' must be a list of component mappings; a null/absent kind yields no refs, but a scalar or mapping is malformed.

Source

Thrown at src/specify_cli/bundler/models/manifest.py:257

def _parse_str_list(raw: Any, field_name: str) -> tuple[str, ...]:
    """Coerce a manifest list-of-strings field into a tuple of strings.

    Rejects a bare string/bytes (which would otherwise be iterated
    character-by-character) and any non-list/tuple, matching the manifest
    contract (``string[]``).
    """
    if raw is None:
        return ()
    if isinstance(raw, (str, bytes)) or not isinstance(raw, (list, tuple)):
        raise BundlerError(f"'{field_name}' must be a list of strings when present.")
    return tuple(str(item) for item in raw)


def _parse_refs(kind: str, raw: Any) -> list[ComponentRef]:
    if raw is None:
        return []
    if not isinstance(raw, list):
        raise BundlerError(f"provides.{kind} must be a list when present.")
    refs: list[ComponentRef] = []
    for item in raw:
        if not isinstance(item, dict):
            raise BundlerError(f"Each provides.{kind} entry must be a mapping.")
        priority = _parse_priority(kind, item.get("priority"))
        refs.append(
            ComponentRef(
                kind=kind,
                id=_text(item.get("id")),
                version=(str(item["version"]).strip() if item.get("version") else None),
                source=(str(item["source"]).strip() if item.get("source") else None),
                priority=priority,
                strategy=(str(item["strategy"]).strip() if item.get("strategy") else None),
            )
        )
    return refs

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Make each provides.<kind> value a list of component mappings.
  2. Remove the kind key if that kind provides nothing.

Example fix

# before (bundle.yml)
provides:
  skills:
    id: my-skill

# after
provides:
  skills:
    - id: my-skill
Defensive patterns

Strategy: validation

Validate before calling

def provides_kind_lists_ok(data: dict) -> bool:
    provides = data.get("provides") or {}
    return all(v is None or isinstance(v, list) for v in provides.values())

Type guard

def is_component_ref_list(raw: object) -> bool:
    return raw is None or (isinstance(raw, list) and all(isinstance(i, dict) for i in raw))

Try / catch

try:
    manifest = BundleManifest.from_file(p)
except BundlerError as e:
    if "must be a list when present" in str(e):
        # wrap the single mapping entry in a list
        ...

Prevention

When it happens

Trigger: A manifest contains 'provides: {skills: {id: my-skill}}' (a single mapping) or 'provides: {skills: my-skill}' (a scalar); _parse_refs(kind, raw) rejects the non-list before iterating entries.

Common situations: Providing a single component and omitting the '-' bullet; mixing up the per-kind list shape with the outer 'provides' mapping shape.

Related errors


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