{"record":{"id":"ebda0b87689e8f95","repo":"github/spec-kit","slug":"failed-to-install-bundle-plan-bundle-id-exc","errorCode":null,"errorMessage":"Failed to install bundle '{plan.bundle_id}': {exc}. No changes were recorded.","messagePattern":"Failed to install bundle '(.+?)': (.+?)\\. No changes were recorded\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/services/installer.py","lineNumber":163,"sourceCode":"            planned = {(c.kind, c.id) for c in plan.components}\n            still_needed = components_still_needed(\n                records, exclude_bundle_id=plan.bundle_id\n            )\n            for component in existing.contributed_components:\n                key = (component.kind, component.id)\n                if key in planned:\n                    continue\n                if key in still_needed:\n                    continue\n                if installer.is_installed(project_root, component):\n                    installer.remove(project_root, component)\n                    result.uninstalled.append(component)\n    except BundlerError:\n        _rollback(project_root, installer, done)\n        raise\n    except Exception as exc:  # noqa: BLE001\n        _rollback(project_root, installer, done)\n        raise BundlerError(\n            f\"Failed to install bundle '{plan.bundle_id}': {exc}. \"\n            \"No changes were recorded.\"\n        ) from exc\n\n    record = InstalledBundleRecord.create(\n        bundle_id=plan.bundle_id,\n        version=plan.version,\n        components=contributed,\n        # Preserve the original install time across refresh/update so\n        # ``bundle list`` keeps reporting when the bundle was first installed.\n        installed_at=existing.installed_at if existing is not None else None,\n    )\n    save_records(project_root, upsert_record(records, record))\n    return result\n\n\ndef remove_bundle(\n    project_root: Path,","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/services/installer.py#L145-L181","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"from specify_cli.bundler.services.installer import load_records\nfrom pathlib import Path\n\n# Pre-flight: artifact readable and no conflicting partial record\nrecords = load_records(project_root)\nassert all(r.bundle_id != plan.bundle_id or replace for r in records)","typeGuard":"def is_bundler_error(exc: BaseException) -> bool:\n    from specify_cli.bundler.core import BundlerError\n    return isinstance(exc, BundlerError)","tryCatchPattern":"from specify_cli.bundler.core import BundlerError\n\ntry:\n    install_bundle(project_root, plan, installer)\nexcept BundlerError as exc:\n    # rollback already ran; exc.__cause__ holds the original exception\n    log.error(\"bundle install failed: %s\", exc, exc_info=exc.__cause__)","preventionTips":["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/."],"tags":["bundler","install","rollback","wrapper-error"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}