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
- Read the text after ': ' and before '. No changes' — it is the original exception; fix that root cause first.
- 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.
- Verify the bundle artifact: run `specify bundle validate <bundle_dir>` and rebuild with `specify bundle build` if the zip is stale or corrupted.
- Check filesystem permissions and free space in the project root and .specify/ directory.
- 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
- Run `specify bundle validate` and rebuild artifacts with `specify bundle build` before installing.
- Treat the text before '. No changes were recorded' as the root cause and fix it, never blind-retry.
- Keep write permissions and disk space available under the project root and .specify/.
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
- Failed to remove bundle '{bundle_id}': {exc}. {detail}
- Failed to {action} {label}.
- Failed to remove preset '{component.id}': {exc}
- Failed to remove extension '{component.id}': {exc}
- Malformed catalog config at {path}: expected a mapping at th
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/ebda0b87689e8f95.
Report an issue: GitHub.