github/spec-kit · error · BundlerError
Failed to remove bundle '{bundle_id}': {exc}. {detail}
Error message
Failed to remove bundle '{bundle_id}': {exc}. {detail} What it means
An unexpected exception occurred while uninstalling a bundle. The message distinguishes how far the removal got: either some components were already removed, a removal was attempted but nothing completed (partial uninstall possible), or nothing was attempted at all (record left unchanged). This tells you whether your project may be in a partially uninstalled state.
Source
Thrown at src/specify_cli/bundler/services/installer.py:224
except Exception as exc: # noqa: BLE001
if result.uninstalled:
detail = (
f"{len(result.uninstalled)} component(s) were already removed "
"before this failure; the bundle record was left unchanged, "
"so the project may be partially uninstalled."
)
elif remove_attempted:
detail = (
"No components were removed, but the failing component may "
"have made partial changes before raising, so the project "
"may be partially uninstalled."
)
else:
detail = (
"No components were removed and no removal was attempted; "
"the bundle record was left unchanged."
)
raise BundlerError(
f"Failed to remove bundle '{bundle_id}': {exc}. {detail}"
) from exc
return result
def _refresh_component(
project_root: Path,
installer: PrimitiveInstaller,
component: ComponentRef,
) -> None:
"""Re-apply an already-installed component to bring it up to its pinned version.
Prefers a primitive-provided ``refresh`` hook when available; otherwise falls
back to a re-install through the existing idempotent install path.
"""
op = getattr(installer, "refresh", None)
if callable(op):View on GitHub (pinned to bf88c9f9a8)
Solutions
- Parse the {detail} clause: 'partially uninstalled' means you must inspect and finish cleanup by hand; 'record was left unchanged' means a plain retry is safe.
- Fix the underlying cause named in {exc} (permissions, missing file, locked file) and re-run `specify bundle remove`.
- After a partial uninstall, remove the leftover components individually (`specify preset remove <id>`, `specify extension remove <id>`) and then re-run the bundle remove to clear the record.
- Verify final state with `specify bundle list` and by checking the component directories.
Defensive patterns
Strategy: try-catch
Validate before calling
from specify_cli.bundler.services.installer import load_records
from pathlib import Path
records = load_records(project_root)
target = next((r for r in records if r.bundle_id == bundle_id), None)
if target is None:
return
for c in target.contributed_components:
# surface likely failures before the loop mutates anything
assert installer.is_installed(project_root, c), f"{c.kind}/{c.id} missing" Try / catch
from specify_cli.bundler.core import BundlerError
try:
result = remove_bundle(project_root, bundle_id, installer)
except BundlerError as exc:
msg = str(exc)
if "partially uninstalled" in msg:
enter_manual_cleanup_mode() # inspect and finish removal by hand
elif "record was left unchanged" in msg:
fix_root_cause_and_retry() # safe to retry after the cause is fixed Prevention
- Never hand-delete preset/extension directories; always remove via the CLI so bundle records stay in sync.
- Branch on the {detail} clause — 'partially uninstalled' requires manual inspection.
- After any failed remove, verify with `specify bundle list` before retrying.
When it happens
Trigger: Calling remove_bundle() where installer.remove() raises for one of target.contributed_components — e.g. a preset/extension manager remove() failing on a missing or locked file. If an earlier component already succeeded, result.removed is non-empty; if the first remove fails, remove_attempted is True with empty removed; if the failure happened before the loop, neither is set.
Common situations: Uninstalling a bundle while one of its extensions/presets has files locked or already deleted by hand; permission errors under .specify or the agent config dirs; a component directory that was manually moved or renamed.
Related errors
- Failed to remove preset '{component.id}': {exc}
- Failed to remove extension '{component.id}': {exc}
- '{target}' is a built-in default source and cannot be delete
- No project-scoped catalog source matching '{target}' was fou
- Failed to install bundle '{plan.bundle_id}': {exc}. No chang
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/40a1f266d4b2c2eb.
Report an issue: GitHub.