github/spec-kit · error · SystemExit

ERROR: SPECIFY_INIT_DIR does not point to an existing direct

Error message

ERROR: SPECIFY_INIT_DIR does not point to an existing directory: {raw}

What it means

In _validate_safe_shared_directory(), an existing component resolves outside the resolved project root. Unlike the symlink checks, this catches non-symlink redirections — junctions, bind mounts, or paths whose canonical location leaves the root once resolved — and refuses to proceed with shared-infra operations through them.

Source

Thrown at scripts/python/common.py:43

        parent = current.parent
        if parent == current:
            return None
        current = parent


def resolve_specify_init_dir() -> Path:
    raw = os.environ.get("SPECIFY_INIT_DIR", "")
    candidate = Path(raw)
    if not candidate.is_absolute():
        candidate = Path.cwd() / candidate
    try:
        init_root = candidate.resolve(strict=True)
    except OSError:
        print(
            f"ERROR: SPECIFY_INIT_DIR does not point to an existing directory: {raw}",
            file=sys.stderr,
        )
        raise SystemExit(1)
    if not init_root.is_dir():
        print(
            f"ERROR: SPECIFY_INIT_DIR does not point to an existing directory: {raw}",
            file=sys.stderr,
        )
        raise SystemExit(1)
    if not (init_root / ".specify").is_dir():
        print(
            "ERROR: SPECIFY_INIT_DIR is not a Spec Kit project "
            f"(no .specify/ directory): {init_root}",
            file=sys.stderr,
        )
        raise SystemExit(1)
    return init_root


def get_repo_root(script_file: Path | None = None) -> Path:
    if os.environ.get("SPECIFY_INIT_DIR"):

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Pass project_path pre-resolved: Path(project).resolve()
  2. Replace junctions/mounts inside the tree with real directories
  3. Move the project to a native filesystem location without redirect semantics
  4. Debug by printing resolve() of each parent vs the root to identify the escaping component

Example fix

# before
run(Path('C:/proj'))  # with a junction inside .specify

# after
run(Path('C:/proj').resolve())  # and: rmdir junction, mkdir real dir
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

root = project_path.resolve()
for parent in rel_dir.parents:
    p = root / parent
    if p.exists():
        assert p.resolve().is_relative_to(root), f'{p} escapes root after resolve'

Try / catch

try:
    _validate_safe_shared_directory(project_path, directory)
except ValueError as e:
    if 'escapes project root' in str(e):
        _validate_safe_shared_directory(Path(project_path).resolve(), directory)
    else:
        raise

Prevention

When it happens

Trigger: A parent component is a Windows junction, bind mount, or mount point that resolve() relocates outside root; or root was passed non-resolved and a component canonicalizes differently (macOS /tmp, WSL host mounts).

Common situations: Windows directory junctions created by mklink /J to save disk; WSL accessing /mnt/c paths; devcontainer bind mounts; project living under symlinked temp dirs.

Related errors


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