{"record":{"id":"d21a2c98bcd6c4c4","repo":"github/spec-kit","slug":"error-feature-directory-not-found-set-specify-fe","errorCode":null,"errorMessage":"ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or ensure .specify/feature.json contains feature_directory.","messagePattern":"ERROR: Feature directory not found\\. Set SPECIFY_FEATURE_DIRECTORY or ensure \\.specify/feature\\.json contains feature_directory\\.","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"scripts/python/common.py","lineNumber":157,"sourceCode":"    repo_root = get_repo_root(script_file)\n    current_branch = get_current_branch()\n\n    feature_dir_raw = os.environ.get(\"SPECIFY_FEATURE_DIRECTORY\", \"\")\n    if feature_dir_raw:\n        feature_dir = Path(feature_dir_raw)\n        if not feature_dir.is_absolute():\n            feature_dir = repo_root / feature_dir\n        if not no_persist:\n            persist_feature_json(repo_root, feature_dir_raw)\n    elif (repo_root / \".specify\" / \"feature.json\").is_file():\n        stored = read_feature_json_feature_directory(repo_root)\n        if not stored:\n            print(\n                \"ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY \"\n                \"or ensure .specify/feature.json contains feature_directory.\",\n                file=sys.stderr,\n            )\n            raise SystemExit(1)\n        feature_dir = Path(stored)\n        if not feature_dir.is_absolute():\n            feature_dir = repo_root / feature_dir\n    else:\n        print(\n            \"ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY \"\n            \"or run the specify command to create .specify/feature.json.\",\n            file=sys.stderr,\n        )\n        raise SystemExit(1)\n\n    if not current_branch:\n        current_branch = Path(_trim_trailing_separators(feature_dir)).name\n\n    return FeaturePaths(\n        repo_root=repo_root,\n        current_branch=current_branch,\n        feature_dir=feature_dir,","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/scripts/python/common.py#L139-L175","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Reinstall or restore the project that owns the workflow (the path in installed_registry_root must exist again), then resume","Re-run the workflow from scratch in the current project with `workflow run` instead of resuming cross-project state","If the root moved, update the persisted run state's installed_registry_root to the new absolute, non-symlinked location","In ephemeral environments, avoid resuming state persisted on a previous container — always run fresh"],"exampleFix":"# before\nresume(installed_registry_root='/tmp/ci-build-42/.specify')  # deleted container dir\n\n# after\n# fresh run in the current, existing project root\nworkflow_run(project_root=Path.cwd(), resume=False)","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef can_resume(installed_registry_root: str | None) -> bool:\n    if not installed_registry_root:\n        return True\n    p = Path(installed_registry_root)\n    return p.is_absolute() and p.is_dir() and not any(\n        (p.joinpath(*p.parts[i+1:])).is_symlink() for i in range(len(p.parts) - 1)\n    )\n\nassert can_resume(state.installed_registry_root), 'owner project missing; run fresh instead'","typeGuard":null,"tryCatchPattern":"try:\n    resume_workflow(run_id, project_root=Path.cwd())\nexcept ValueError as e:\n    if 'workflow owner is unavailable' in str(e):\n        workflow_run_fresh()  # start a new run in the current project\n    else:\n        raise","preventionTips":["Don't move or delete the project that installed a workflow while runs are resumable","In ephemeral CI, use fresh runs instead of resuming state from prior containers","If a root moved intentionally, update the persisted installed_registry_root to the new path","Mount persistent volumes at stable absolute paths if cross-run resume is required"],"tags":["workflow","state-management","filesystem"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}