{"record":{"id":"5e18022f76046395","repo":"github/spec-kit","slug":"manifest-path-is-not-a-regular-file-rel","errorCode":null,"errorMessage":"Manifest path is not a regular file: {rel}","messagePattern":"Manifest path is not a regular file: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/integrations/manifest.py","lineNumber":221,"sourceCode":"                f\"Manifest paths must be canonical; '..' segments are not \"\n                f\"allowed (got {rel})\"\n            )\n        # Walk each path component before resolution so a symlinked ancestor\n        # (e.g. ``linked_dir/file.txt`` where ``linked_dir`` is a symlink)\n        # cannot be silently followed by ``_validate_rel_path().resolve()``\n        # down to a target outside the project root. ``_ensure_safe_manifest_directory``\n        # uses the same pattern.\n        _walk = self.project_root\n        for part in rel.parts:\n            _walk = _walk / part\n            if _walk.is_symlink():\n                raise ValueError(\n                    f\"Refusing to record symlinked manifest path: {rel} \"\n                    f\"(symlinked at {_walk.relative_to(self.project_root).as_posix()})\"\n                )\n        abs_path = _validate_rel_path(rel, self.project_root)\n        if not abs_path.is_file():\n            raise ValueError(\n                f\"Manifest path is not a regular file: {rel}\"\n            )\n        normalized = abs_path.relative_to(self.project_root).as_posix()\n        self._files[normalized] = _sha256(abs_path)\n        if recovered:\n            self._recovered_files.add(normalized)\n        else:\n            # ``recovered=False`` means the caller is asserting this path is\n            # managed-baseline now, not merely observed; drop any stale\n            # recovered marker so future is_recovered() queries reflect the\n            # transition. ``discard`` is a no-op when the key is absent.\n            self._recovered_files.discard(normalized)\n\n    def remove(self, rel_path: str | Path) -> bool:\n        \"\"\"Drop *rel_path* from the tracked file set and any recovered marker.\n\n        Operates purely on the manifest's recorded key; it does NOT touch the\n        file on disk. Returns ``True`` if an entry was present and removed.","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/manifest.py#L203-L239","documentation":"Raised by record_file()/record_existing() after validation passes but abs_path.is_file() is false — the path does not exist or is not a regular file (directory, FIFO, broken link already rejected earlier). The manifest records SHA-256 hashes, so it can only track real files that exist at record time.","triggerScenarios":"Calling record_file() before the file was written (ordering bug in custom setup() logic), recording a path that is a directory, or a TOCTOU deletion between validation and the is_file() check.","commonSituations":"Custom integrations whose setup() writes files after calling record_file(); template rendering that silently failed; typos in the rel_path so the intended file never matched.","solutions":["Verify ordering: create/write the file first, then manifest.record_file(rel)","Check the path exists and is a file before recording (see validation snippet)","If the file legitimately may be absent, skip recording instead of erroring"],"exampleFix":"// before\nmanifest.record_file(\".claude/commands/build.md\")\n(dest).write_text(content)\n// after\n(dest).write_text(content)\nmanifest.record_file(\".claude/commands/build.md\")","handlingStrategy":"validation","validationCode":"target = project_root / rel\nif not target.is_file():\n    raise RuntimeError(f\"file not written yet: {rel}\")\nmanifest.record_file(rel)","typeGuard":null,"tryCatchPattern":"try:\n    manifest.record_file(rel)\nexcept ValueError as exc:\n    if \"not a regular file\" in str(exc):\n        write_file_then_record(rel)  # fix ordering, retry once\n    else:\n        raise","preventionTips":["In custom setup(), write the file before record_file()","Check dest.is_file() right before recording","Treat silent template-render failures as hard errors"],"tags":["manifest","file-existence","ordering"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}