github/spec-kit · error · BundlerError

Failed to remove preset '{component.id}': {exc}

Error message

Failed to remove preset '{component.id}': {exc}

What it means

During bundle removal, PresetManager.remove(component.id) raised an unexpected exception, which the bundler wraps as BundlerError with the original message preserved. The caller (remove_bundle) will then classify the removal state (see error 122) — this preset was the component that failed.

Source

Thrown at src/specify_cli/bundler/services/primitives.py:223

            )
        _assert_pinned_version(
            "Preset", component.id, component.version, info.get("version")
        )
        zip_path = catalog.download_pack(component.id)
        try:
            self._manager.install_from_zip(
                zip_path, speckit_version, priority, **({"force": True} if force else {})
            )
        finally:
            with contextlib.suppress(Exception):
                if zip_path.exists():
                    zip_path.unlink()

    def remove(self, component: ComponentRef) -> None:
        try:
            self._manager.remove(component.id)
        except Exception as exc:  # noqa: BLE001
            raise BundlerError(
                f"Failed to remove preset '{component.id}': {exc}"
            ) from exc


class _ExtensionKindManager:
    def __init__(self, project_root: Path, allow_network: bool) -> None:
        from ...extensions import ExtensionManager

        self._root = project_root
        self._allow_network = allow_network
        self._manager = ExtensionManager(project_root)

    def is_installed(self, component: ComponentRef) -> bool:
        try:
            return self._manager.registry.is_installed(component.id)
        except Exception:  # noqa: BLE001
            return False

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Read the embedded {exc} to identify the concrete failure (missing path vs permission).
  2. If the preset is already gone, restore state consistency: reinstall the preset (`specify preset add <id>`) then remove the bundle, or clean up its record manually.
  3. Fix permissions on the preset directory and retry the bundle remove.
  4. Finish with `specify bundle list` to confirm the bundle record is gone.
Defensive patterns

Strategy: try-catch

Validate before calling

from specify_cli.presets import PresetManager

manager = PresetManager(project_root)
for c in bundle_preset_components:
    if not manager.is_installed(c.id):  # adjust to actual API
        print(f"preset {c.id} missing; state may be out of sync")

Try / catch

from specify_cli.bundler.core import BundlerError

try:
    remove_bundle(project_root, bundle_id, installer)
except BundlerError as exc:
    if "Failed to remove preset" in str(exc):
        # exc.__cause__ holds PresetManager.remove's original exception
        reconcile_preset_state(str(exc.__cause__))

Prevention

When it happens

Trigger: Uninstalling a bundle whose preset component's remove() throws — preset files already deleted by hand, .specify/presets state out of sync, or permission errors on the preset directory.

Common situations: User manually deleted a preset directory before running bundle remove; two tools managing the same preset; read-only filesystem mount.

Related errors


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