github/spec-kit · error · BundlerError

'{field_name}' must be a list of strings when present.

Error message

'{field_name}' must be a list of strings when present.

What it means

Raised by the manifest helper _parse_str_list when a list-of-strings field (e.g. requires.tools, requires.mcp, or tags) is a bare string/bytes or any non-list/tuple value. The explicit string/bytes check prevents a bare string from being iterated character-by-character, which would silently produce garbage entries like 'g','i','t'.

Source

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

    was silently accepted and the bundle shipped ``"None"`` as its
    author/license/description.
    """
    if raw is None:
        return ""
    return str(raw).strip()


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),

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Convert the value to a YAML list, even for a single item.
  2. Remove the key if the list should be empty (absence/null returns ()).

Example fix

# before (bundle.yml)
tools: git

# after
tools:
  - git
Defensive patterns

Strategy: validation

Validate before calling

def str_list_ok(value: object) -> bool:
    return value is None or (
        isinstance(value, (list, tuple))
        and not isinstance(value, (str, bytes))
    )

Type guard

def is_str_list(raw: object) -> bool:
    return raw is None or (isinstance(raw, list) and all(isinstance(x, str) for x in raw))

Try / catch

try:
    manifest = BundleManifest.from_file(p)
except BundlerError as e:
    if "list of strings" in str(e):
        # convert bare strings (tools/tags/mcp) to single-item YAML lists
        ...

Prevention

When it happens

Trigger: A manifest with 'tools: git' or 'tags: productivity' (bare strings instead of YAML lists) is parsed; _parse_str_list rejects the str before tuple(str(item) for item in raw) can iterate characters.

Common situations: Writing a single-item list without the '-' bullet — the most common YAML shorthand mistake; quoting a comma-separated string instead of a sequence.

Related errors


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