github/spec-kit · error · ValueError
Refusing to overwrite symlinked integration manifest path: {
Error message
Refusing to overwrite symlinked integration manifest path: {label} What it means
Raised by _ensure_safe_manifest_destination when the manifest file path itself (.specify/integrations/<key>.manifest.json) is a symlink. Overwriting a symlinked file would follow the link and could clobber a file outside the project, so the write is refused up front.
Source
Thrown at src/specify_cli/integrations/manifest.py:94
try:
current.resolve().relative_to(root_resolved)
except (OSError, ValueError):
raise ValueError(f"Integration manifest directory escapes project root: {label}") from None
continue
current.mkdir()
try:
current.resolve().relative_to(root_resolved)
except (OSError, ValueError):
raise ValueError(f"Integration manifest directory escapes project root: {label}") from None
def _ensure_safe_manifest_destination(root: Path, path: Path) -> None:
"""Refuse manifest writes that would escape the project or follow symlinks."""
root_resolved = root.resolve()
_ensure_safe_manifest_directory(root, path.parent)
label = _manifest_path_label(root, path)
if path.is_symlink():
raise ValueError(f"Refusing to overwrite symlinked integration manifest path: {label}")
if path.exists():
if not path.is_file():
raise ValueError(f"Integration manifest path is not a file: {label}")
try:
path.resolve().relative_to(root_resolved)
except (OSError, ValueError):
raise ValueError(f"Integration manifest path escapes project root: {label}") from None
class IntegrationManifest:
"""Tracks files installed by a single integration.
Parameters:
key: Integration identifier (e.g. ``"copilot"``).
project_root: Absolute path to the project directory.
version: CLI version string recorded in the manifest.
resolve_project_root: Resolve ``project_root`` before using it.
"""View on GitHub (pinned to bf88c9f9a8)
Solutions
- Delete the symlink; save() will write a regular file (rm .specify/integrations/<key>.manifest.json)
- If you need the file elsewhere, copy instead of symlink after the CLI writes it
- Configure the dotfile manager to ignore .specify/integrations
Example fix
# before ln -s ~/shared/claude.manifest.json .specify/integrations/claude.manifest.json # after rm .specify/integrations/claude.manifest.json # save() recreates a regular file
Defensive patterns
Strategy: validation
Validate before calling
mp = manifest.manifest_path
if mp.is_symlink():
mp.unlink() # let save() write a regular file Try / catch
try:
manifest.save()
except ValueError as exc:
if "symlinked integration manifest path" in str(exc):
manifest.manifest_path.unlink()
manifest.save()
else:
raise Prevention
- Never symlink manifest JSON files across worktrees
- Tell dotfile managers to exclude .specify/integrations
- Copy shared manifests instead of linking them
When it happens
Trigger: IntegrationManifest.save() (or any write to manifest_path) when <key>.manifest.json is a symlink — commonly pointing at a shared manifest in a dotfiles repo or another worktree.
Common situations: Developers sharing manifests across worktrees via symlinks; dotfile managers (stow, chezmoi) that link config files; CI restoring cached manifests as links.
Related errors
- Refusing to use symlinked integration manifest directory: {l
- Path {rel} resolves to {resolved} which is outside the proje
- Integration manifest path escapes project root: {label}
- Refusing to record symlinked manifest path: {rel} (symlinked
- Error: --short-name requires a value
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/754a402ec0e238b5.
Report an issue: GitHub.