github/spec-kit · error · PresetError
Preset '{manifest.id}' is already installed. Use 'specify pr
Error message
Preset '{manifest.id}' is already installed. Use 'specify preset remove {manifest.id}' first. What it means
install_from_directory refuses to install a preset whose manifest id is already present in the registry, unless force=True. This prevents silently overwriting an installed preset; the message points at the removal command. With force=True the manager instead removes the existing installation and continues.
Source
Thrown at src/specify_cli/presets/__init__.py:3567
Returns:
Installed preset manifest
Raises:
PresetValidationError: If manifest is invalid or priority is invalid
PresetCompatibilityError: If pack is incompatible
"""
# Validate priority
if priority < 1:
raise PresetValidationError("Priority must be a positive integer (1 or higher)")
manifest_path = source_dir / "preset.yml"
manifest = PresetManifest(manifest_path)
self.check_compatibility(manifest, speckit_version)
if self.registry.is_installed(manifest.id):
if not force:
raise PresetError(
f"Preset '{manifest.id}' is already installed. "
f"Use 'specify preset remove {manifest.id}' first."
)
self.remove(manifest.id)
dest_dir = self.presets_dir / manifest.id
if dest_dir.exists():
shutil.rmtree(dest_dir)
shutil.copytree(source_dir, dest_dir)
# Pre-register the preset so that composition resolution can see it
# in the priority stack when resolving composed command content.
self.registry.add(manifest.id, {
"version": manifest.version,
"source": "local",
"manifest_hash": manifest.get_hash(),
"enabled": True,View on GitHub (pinned to bf88c9f9a8)
Solutions
- Remove first: `specify preset remove <id>` (as the message says), then reinstall.
- Or pass force=True to the programmatic call for idempotent installs.
- If the preset should not be registered, check for leftover registry entries from a failed install and clean them.
Example fix
# before manager.install_from_directory(src, ver) # after manager.install_from_directory(src, ver, force=True)
Defensive patterns
Strategy: fallback
Validate before calling
if manager.registry.is_installed(pack_id):
manager.remove(pack_id) # or choose to skip
manager.install_from_directory(source_dir, speckit_version, priority=priority) Try / catch
try:
manager.install_from_directory(src, ver)
except PresetError as e:
if "already installed" in str(e):
manager.install_from_directory(src, ver, force=True) Prevention
- Make install scripts idempotent: check is_installed() or pass force=True.
- During preset development, wrap remove+install in one script to avoid stale state.
- After a failed install, verify registry state before retrying.
When it happens
Trigger: Running a second `specify preset add <same-id>` (or calling install_from_directory without force=True) while the preset id exists in .specify/presets/registry.json — including when the previous install partially failed but registered the id.
Common situations: Re-running an install script/idempotent CI step, iterating on a local preset during development, or reinstalling after a failed first attempt that had already pre-registered the entry.
Related errors
- Preset '{pack_id}' not found in registry
- Priority must be a positive integer (1 or higher)
- No preset.yml found in archive
- Failed to parse preset manifest {manifest_path}: {exc}
- Wrap layer {path} is missing {placeholder}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/668877fefb4f2734.
Report an issue: GitHub.