github/spec-kit · error · BundlerError

Failed to install bundle '{plan.bundle_id}': {exc}. No chang

Error message

Failed to install bundle '{plan.bundle_id}': {exc}. No changes were recorded.

What it means

A bundle install failed because an unexpected (non-Bundler) exception was raised mid-install. The installer already ran its rollback (_rollback over the components completed so far), so no partial install is recorded in .specify, and the original exception is chained via `raise ... from exc`. The message embeds the underlying exception text ({exc}) so the root cause is visible.

Source

Thrown at src/specify_cli/bundler/services/installer.py:163

            planned = {(c.kind, c.id) for c in plan.components}
            still_needed = components_still_needed(
                records, exclude_bundle_id=plan.bundle_id
            )
            for component in existing.contributed_components:
                key = (component.kind, component.id)
                if key in planned:
                    continue
                if key in still_needed:
                    continue
                if installer.is_installed(project_root, component):
                    installer.remove(project_root, component)
                    result.uninstalled.append(component)
    except BundlerError:
        _rollback(project_root, installer, done)
        raise
    except Exception as exc:  # noqa: BLE001
        _rollback(project_root, installer, done)
        raise BundlerError(
            f"Failed to install bundle '{plan.bundle_id}': {exc}. "
            "No changes were recorded."
        ) from exc

    record = InstalledBundleRecord.create(
        bundle_id=plan.bundle_id,
        version=plan.version,
        components=contributed,
        # Preserve the original install time across refresh/update so
        # ``bundle list`` keeps reporting when the bundle was first installed.
        installed_at=existing.installed_at if existing is not None else None,
    )
    save_records(project_root, upsert_record(records, record))
    return result


def remove_bundle(
    project_root: Path,

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Read the text after ': ' and before '. No changes' — it is the original exception; fix that root cause first.
  2. If the message mentions a specific component id, try installing that component alone with its own CLI (e.g. `specify preset add <id>`) to reproduce the failure in isolation.
  3. Verify the bundle artifact: run `specify bundle validate <bundle_dir>` and rebuild with `specify bundle build` if the zip is stale or corrupted.
  4. Check filesystem permissions and free space in the project root and .specify/ directory.
  5. Re-run `specify bundle list` (or load_records) to confirm the rollback left no half-recorded bundle before retrying.
Defensive patterns

Strategy: try-catch

Validate before calling

from specify_cli.bundler.services.installer import load_records
from pathlib import Path

# Pre-flight: artifact readable and no conflicting partial record
records = load_records(project_root)
assert all(r.bundle_id != plan.bundle_id or replace for r in records)

Type guard

def is_bundler_error(exc: BaseException) -> bool:
    from specify_cli.bundler.core import BundlerError
    return isinstance(exc, BundlerError)

Try / catch

from specify_cli.bundler.core import BundlerError

try:
    install_bundle(project_root, plan, installer)
except BundlerError as exc:
    # rollback already ran; exc.__cause__ holds the original exception
    log.error("bundle install failed: %s", exc, exc_info=exc.__cause__)

Prevention

When it happens

Trigger: Calling install_bundle() (or the `specify bundle install` CLI) where one of the per-component installer.install() calls raises anything other than BundlerError — e.g. a corrupted zip in the artifact, an OSError while copying files, or a crash inside a delegated preset/extension/workflow installer that is not a typer.Exit. The generic `except Exception` branch at installer.py:160 catches it and re-raises as BundlerError.

Common situations: Installing a hand-built or corrupted bundle zip; a component source directory with unreadable/missing files; disk-full or permission errors during component copy; a third-party preset/extension whose install code raises a plain exception.

Related errors


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