github/spec-kit · error · SystemExit

ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRE

Error message

ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or ensure .specify/feature.json contains feature_directory.

What it means

When resuming a workflow run, _resolve_resume_root() rejects a persisted installed_registry_root that is absolute, symlink-free, but no longer an existing directory. The run state lives in the installing project; a vanished root cannot be safely rediscovered, and falling back to the current project would read state from an unrelated tree, so it fails closed with ValueError.

Source

Thrown at scripts/python/common.py:157

    repo_root = get_repo_root(script_file)
    current_branch = get_current_branch()

    feature_dir_raw = os.environ.get("SPECIFY_FEATURE_DIRECTORY", "")
    if feature_dir_raw:
        feature_dir = Path(feature_dir_raw)
        if not feature_dir.is_absolute():
            feature_dir = repo_root / feature_dir
        if not no_persist:
            persist_feature_json(repo_root, feature_dir_raw)
    elif (repo_root / ".specify" / "feature.json").is_file():
        stored = read_feature_json_feature_directory(repo_root)
        if not stored:
            print(
                "ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY "
                "or ensure .specify/feature.json contains feature_directory.",
                file=sys.stderr,
            )
            raise SystemExit(1)
        feature_dir = Path(stored)
        if not feature_dir.is_absolute():
            feature_dir = repo_root / feature_dir
    else:
        print(
            "ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY "
            "or run the specify command to create .specify/feature.json.",
            file=sys.stderr,
        )
        raise SystemExit(1)

    if not current_branch:
        current_branch = Path(_trim_trailing_separators(feature_dir)).name

    return FeaturePaths(
        repo_root=repo_root,
        current_branch=current_branch,
        feature_dir=feature_dir,

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Reinstall or restore the project that owns the workflow (the path in installed_registry_root must exist again), then resume
  2. Re-run the workflow from scratch in the current project with `workflow run` instead of resuming cross-project state
  3. If the root moved, update the persisted run state's installed_registry_root to the new absolute, non-symlinked location
  4. In ephemeral environments, avoid resuming state persisted on a previous container — always run fresh

Example fix

# before
resume(installed_registry_root='/tmp/ci-build-42/.specify')  # deleted container dir

# after
# fresh run in the current, existing project root
workflow_run(project_root=Path.cwd(), resume=False)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def can_resume(installed_registry_root: str | None) -> bool:
    if not installed_registry_root:
        return True
    p = Path(installed_registry_root)
    return p.is_absolute() and p.is_dir() and not any(
        (p.joinpath(*p.parts[i+1:])).is_symlink() for i in range(len(p.parts) - 1)
    )

assert can_resume(state.installed_registry_root), 'owner project missing; run fresh instead'

Try / catch

try:
    resume_workflow(run_id, project_root=Path.cwd())
except ValueError as e:
    if 'workflow owner is unavailable' in str(e):
        workflow_run_fresh()  # start a new run in the current project
    else:
        raise

Prevention

When it happens

Trigger: `workflow resume` (or API equivalents reading installed_registry_root) where the recorded registry root was deleted, the checkout was moved, the volume was unmounted, or the path was recorded on another machine/container that no longer exists.

Common situations: Deleting or moving the project that installed the workflow; ephemeral CI containers where /home/... roots vanish between runs; unmounting network volumes; cloning a run-state file between machines; switching between Docker images with different layouts.

Related errors


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