github/spec-kit · error · ValueError

manifest.project_root

Error message

manifest.project_root ({manifest.project_root}) does not match project_root ({project_root_resolved})

What it means

Every template-based setup() (this one in the Markdown command path) first cross-checks that the IntegrationManifest's recorded project_root equals project_root.resolve(). The manifest is created at init time with the resolved absolute path of the project; a mismatch means the project directory has moved, been renamed, or is being addressed via a different (e.g. symlinked) path than when it was initialized. Setup refuses to continue to avoid recording manifest entries against the wrong root.

Solutions

  1. Always invoke specify from the same canonical project path — the resolved one (if you use a symlink, use it consistently, though resolving is safer).
  2. After moving a project, re-initialize or refresh the manifest (delete .specify/integrations/<key>.manifest.json and re-run the integration setup) so it records the new root.
  3. In CI, pin the checkout directory to a stable absolute path across steps.

Example fix

# before: initialized at /old/path, now run from /new/path
$ cd /new/path && specify ...  # -> mismatch error
# after: re-record the manifest at the new root
$ rm .specify/integrations/<key>.manifest.json && specify ...  # re-run setup
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def manifest_root_matches(project_root: Path, manifest) -> bool:
    return manifest.project_root == project_root.resolve()

Try / catch

try:
    integration.setup(project_root, manifest)
except ValueError as exc:
    if "manifest.project_root" in str(exc):
        # project moved: drop the stale manifest and re-setup at this location
        (project_root / ".specify" / "integrations" / f"{integration.key}.manifest.json").unlink(missing_ok=True)
        integration.setup(project_root, fresh_manifest)
    else:
        raise

Prevention

When it happens

Trigger: Running `specify` commands after mv-ing or cloning the project to a new location; passing a symlinked path to the project on one run and the real path on another; a stale .specify/integrations manifest carried over by copying the project directory.

Common situations: Renaming a workspace folder mid-project; CI checking out to differing absolute paths between the init step and later steps; users following a symlinked home path (~/code vs /Users/name/code) inconsistently.

Related errors


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

Appendix: source

Thrown at src/specify_cli/integrations/base.py:1042

            args.extend(["--model", model])
        if output_json:
            args.extend(["--output-format", "json"])
        return args

    def setup(
        self,
        project_root: Path,
        manifest: IntegrationManifest,
        parsed_options: dict[str, Any] | None = None,
        **opts: Any,
    ) -> list[Path]:
        templates = self.list_command_templates()
        if not templates:
            return []

        project_root_resolved = project_root.resolve()
        if manifest.project_root != project_root_resolved:
            raise ValueError(
                f"manifest.project_root ({manifest.project_root}) does not match "
                f"project_root ({project_root_resolved})"
            )

        dest = self.commands_dest(project_root).resolve()
        try:
            dest.relative_to(project_root_resolved)
        except ValueError as exc:
            raise ValueError(
                f"Integration destination {dest} escapes "
                f"project root {project_root_resolved}"
            ) from exc
        dest.mkdir(parents=True, exist_ok=True)

        script_type = opts.get("script_type", "sh")
        arg_placeholder = (
            self.registrar_config.get("args", "$ARGUMENTS")
            if self.registrar_config

View on GitHub (pinned to bf88c9f9a8)