github/spec-kit · error · ValueError
Manifest path is not a regular file: {rel}
Error message
Manifest path is not a regular file: {rel} What it means
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.
Source
Thrown at src/specify_cli/integrations/manifest.py:221
f"Manifest paths must be canonical; '..' segments are not "
f"allowed (got {rel})"
)
# Walk each path component before resolution so a symlinked ancestor
# (e.g. ``linked_dir/file.txt`` where ``linked_dir`` is a symlink)
# cannot be silently followed by ``_validate_rel_path().resolve()``
# down to a target outside the project root. ``_ensure_safe_manifest_directory``
# uses the same pattern.
_walk = self.project_root
for part in rel.parts:
_walk = _walk / part
if _walk.is_symlink():
raise ValueError(
f"Refusing to record symlinked manifest path: {rel} "
f"(symlinked at {_walk.relative_to(self.project_root).as_posix()})"
)
abs_path = _validate_rel_path(rel, self.project_root)
if not abs_path.is_file():
raise ValueError(
f"Manifest path is not a regular file: {rel}"
)
normalized = abs_path.relative_to(self.project_root).as_posix()
self._files[normalized] = _sha256(abs_path)
if recovered:
self._recovered_files.add(normalized)
else:
# ``recovered=False`` means the caller is asserting this path is
# managed-baseline now, not merely observed; drop any stale
# recovered marker so future is_recovered() queries reflect the
# transition. ``discard`` is a no-op when the key is absent.
self._recovered_files.discard(normalized)
def remove(self, rel_path: str | Path) -> bool:
"""Drop *rel_path* from the tracked file set and any recovered marker.
Operates purely on the manifest's recorded key; it does NOT touch the
file on disk. Returns ``True`` if an entry was present and removed.View on GitHub (pinned to bf88c9f9a8)
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
Example fix
// before
manifest.record_file(".claude/commands/build.md")
(dest).write_text(content)
// after
(dest).write_text(content)
manifest.record_file(".claude/commands/build.md") Defensive patterns
Strategy: validation
Validate before calling
target = project_root / rel
if not target.is_file():
raise RuntimeError(f"file not written yet: {rel}")
manifest.record_file(rel) Try / catch
try:
manifest.record_file(rel)
except ValueError as exc:
if "not a regular file" in str(exc):
write_file_then_record(rel) # fix ordering, retry once
else:
raise Prevention
- In custom setup(), write the file before record_file()
- Check dest.is_file() right before recording
- Treat silent template-render failures as hard errors
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Manifest must be a YAML mapping at the top level.
- Manifest is missing the required 'bundle' mapping.
- 'requires' must be a mapping when present.
- 'integration' must be a mapping when present.
- 'provides' must be a mapping when present.
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/5e18022f76046395.
Report an issue: GitHub.