github/spec-kit · error · ValueError

Integration manifest path is not a file: {label}

Error message

Integration manifest path is not a file: {label}

What it means

Raised when the destination manifest path exists but is not a regular file (e.g. it is a directory, FIFO, or device). The manifest writer only replaces regular files and refuses anything else.

Source

Thrown at src/specify_cli/integrations/manifest.py:97

                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.
    """

    def __init__(
        self,

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Check the path from the error message: ls -la .specify/integrations/<key>.manifest.json
  2. If it is a directory, remove it (rm -rf) and re-run the specify command
  3. If it holds wanted data, move it aside first

Example fix

# before
.specify/integrations/claude.manifest.json/   # a directory
# after
rm -rf .specify/integrations/claude.manifest.json  # then re-run install
Defensive patterns

Strategy: validation

Validate before calling

mp = manifest.manifest_path
assert not (mp.exists() and not mp.is_file()), f"non-file at {mp}"

Try / catch

try:
    manifest.save()
except ValueError as exc:
    if "not a file" in str(exc):
        shutil.rmtree(manifest.manifest_path, ignore_errors=True)
        manifest.save()
    else:
        raise

Prevention

When it happens

Trigger: A directory named <key>.manifest.json exists under .specify/integrations when save() runs — usually the residue of a bad copy, a template that rendered a directory where a file belongs, or an interrupted install that left a partial tree.

Common situations: Restoring from a backup that dropped the trailing filename and created a directory; rsync/cp mistakes; concurrent or half-failed installs.

Related errors


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