github/spec-kit · error · KeyError
Preset '{pack_id}' not found in registry
Error message
Preset '{pack_id}' not found in registry What it means
PresetRegistry.update_metadata raises KeyError when the given pack_id is not a key under the registry's `presets` mapping, or when the registry's `presets` field is not a dict at all (corrupted registry file). It is a programming-contract error: update only works on already-installed presets.
Source
Thrown at src/specify_cli/presets/__init__.py:629
self._save()
def update(self, pack_id: str, updates: dict):
"""Update preset metadata in registry.
Merges the provided updates with the existing entry, preserving any
fields not specified. The installed_at timestamp is always preserved
from the original entry.
Args:
pack_id: Preset ID
updates: Partial metadata to merge into existing metadata
Raises:
KeyError: If preset is not installed
"""
packs = self.data.get("presets")
if not isinstance(packs, dict) or pack_id not in packs:
raise KeyError(f"Preset '{pack_id}' not found in registry")
existing = packs[pack_id]
# Handle corrupted registry entries (e.g., string/list instead of dict)
if not isinstance(existing, dict):
existing = {}
# Merge: existing fields preserved, new fields override (deep copy to prevent caller mutation)
merged = {**existing, **copy.deepcopy(updates)}
# Always preserve original installed_at based on key existence, not truthiness,
# to handle cases where the field exists but may be falsy (legacy/corruption)
if "installed_at" in existing:
merged["installed_at"] = existing["installed_at"]
else:
# If not present in existing, explicitly remove from merged if caller provided it
merged.pop("installed_at", None)
packs[pack_id] = merged
self._save()
def restore(self, pack_id: str, metadata: dict):
"""Restore preset metadata to registry without modifying timestamps.View on GitHub (pinned to bf88c9f9a8)
Solutions
- Check installation first: `registry.is_installed(pack_id)` or `specify preset list` before updating metadata.
- If the registry file is corrupted, restore .specify/presets/registry.json from version control or recreate it by reinstalling presets.
- Wrap programmatic update_metadata calls in try/except KeyError if the preset's presence is not guaranteed.
Example fix
# before
registry.update_metadata(pack_id, {"last_used": now})
# after
if not registry.is_installed(pack_id):
raise RuntimeError(f"cannot update metadata: {pack_id} not installed")
registry.update_metadata(pack_id, {"last_used": now}) Defensive patterns
Strategy: type-guard
Validate before calling
if not registry.is_installed(pack_id):
raise RuntimeError(f"preset {pack_id} not installed; cannot update metadata")
registry.update_metadata(pack_id, updates) Type guard
def can_update(registry, pack_id: str) -> bool:
packs = registry.data.get("presets")
return isinstance(packs, dict) and pack_id in packs Try / catch
try:
registry.update_metadata(pack_id, updates)
except KeyError:
# preset not installed (or registry corrupted): reinstall then retry
... Prevention
- Always gate update_metadata with is_installed().
- Keep .specify/presets/registry.json in version control so corruption is recoverable.
- Treat a missing entry after 'preset remove' as expected, not exceptional.
When it happens
Trigger: Calling PresetRegistry.update_metadata('some-id', {...}) where 'some-id' was never installed, was removed, or the registry JSON at .specify/presets/registry.json has `presets` as a string/list instead of an object.
Common situations: Calling update right after a failed install (so the entry was never written), using a stale pack_id after `specify preset remove`, or a hand-edited/corrupted registry.json.
Related errors
- Extension '{extension_id}' is not installed
- Cannot restore '{pack_id}': metadata must be a dict
- Preset '{manifest.id}' is already installed. Use 'specify pr
- 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/7d79dd19b30f0f40.
Report an issue: GitHub.