github/spec-kit · error · BundlerError

Invalid YAML in bundle.yml inside '{candidate}': {exc}

Error message

Invalid YAML in bundle.yml inside '{candidate}': {exc}

What it means

The zip branch parses bundle.yml inline with yaml.safe_load; a YAMLError (syntax error, bad indentation, duplicate keys per safe_load rules, tab characters) is converted to BundlerError. Without this wrap the raw YAMLError — neither ValueError nor OSError — would escape bundle_install()'s except BundlerError as a traceback, so this keeps error reporting on-contract.

Source

Thrown at src/specify_cli/commands/bundle/__init__.py:812

        # treatment as yamlio.load_yaml: decode as UTF-8 explicitly —
        # feeding PyYAML the byte stream would let its Reader auto-detect
        # a UTF-16 BOM and accept a manifest the directory and bundle.yml
        # sources reject.
        try:
            text = raw.decode("utf-8")
        except UnicodeError as exc:
            raise BundlerError(
                f"Could not read bundle.yml inside '{candidate}': {exc}"
            ) from exc
        try:
            data = _yaml.safe_load(text)
        except _yaml.YAMLError as exc:
            # The sibling directory/bundle.yml branches reach YAML through
            # load_yaml(), which turns a parse failure into a BundlerError. This
            # branch parses inline, so without this it raises a raw YAMLError --
            # neither a ValueError nor an OSError -- which escapes
            # bundle_install()'s `except BundlerError` as a traceback.
            raise BundlerError(
                f"Invalid YAML in bundle.yml inside '{candidate}': {exc}"
            ) from exc
        return BundleManifest.from_dict(data)

    if candidate.name == "bundle.yml" or candidate.suffix in (".yml", ".yaml"):
        return BundleManifest.from_file(candidate)

    raise BundlerError(
        f"'{candidate}' is not a recognised bundle source (.zip artifact, bundle "
        "directory, or bundle.yml)."
    )


def _resolve_manifest_path(path: Path | None) -> Path:
    target = (path or Path.cwd()).resolve()
    if target.is_dir():
        target = target / "bundle.yml"
    if not target.exists():

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Run a local syntax check: python -c "import yaml,sys; yaml.safe_load(open('bundle.yml'))" and fix the reported line/column.
  2. Replace tabs with spaces, quote values containing colons, and re-zip the artifact.
  3. Validate the manifest outside the zip first (specify bundle install ./my-bundle with the same file) to iterate faster.

Example fix

# before (bundle.yml, tab-indented)
bundle:
	id: my-bundle

# after
bundle:
  id: my-bundle
Defensive patterns

Strategy: validation

Validate before calling

import yaml

text = open("bundle.yml", encoding="utf-8").read()
try:
    yaml.safe_load(text)
except yaml.YAMLError as exc:
    raise SystemExit(f"bundle.yml syntax error: {exc}")

Type guard

def is_valid_yaml(text: str) -> bool:
    import yaml
    try:
        yaml.safe_load(text)
        return True
    except yaml.YAMLError:
        return False

Try / catch

try:
    manifest = _local_manifest_source(Path("bundle.zip"))
except BundlerError as exc:
    if "Invalid YAML in bundle.yml" in str(exc):
        # fix the reported line, re-zip, retry
        ...

Prevention

When it happens

Trigger: A bundle.yml inside a .zip with tab indentation, an unclosed quote/bracket, or malformed frontmatter; specify bundle install ./bundle.zip triggers the inline parse path.

Common situations: Tabs pasted from terminals, unquoted strings containing ':', flow-style lists with missing commas, hand-edited manifests zipped without validation.

Related errors


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