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
- Convert the value to a YAML list, even for a single item.
- 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
- Even single items need the '- ' bullet in tools/mcp/tags.
- Never pass a comma-separated string where a list is expected.
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
- Manifest must be a YAML mapping at the top level.
- Manifest is missing the required 'bundle' mapping.
- 'requires' must be a mapping when present.
- 'integration' must be a mapping when present.
- 'provides' must be a mapping when present.
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/646525cb8fa48a67.
Report an issue: GitHub.