affaan-m/ECC · error · ValueError
Not a regular file
Error message
Not a regular file: {path} What it means
_fingerprint verifies via stat (and again via fstat after opening with O_NOFOLLOW) that the target is a regular file before hashing it. Directories, FIFOs, sockets, device nodes, and files swapped to non-regular between checks raise ValueError. This guarantees the fingerprint describes actual file content.
Solutions
- Check the path with Path(p).is_file() and pass regular files only.
- If you passed a directory, list it and fingerprint each file individually.
- Stop whatever process is exposing the path as a pipe/socket and produce a regular file.
- Re-run ingestion if the file was swapped concurrently; use stable output directories.
Example fix
// before
ingest_assets(sp, paths=["/renders"]) # directory
// after
from pathlib import Path
files = [str(p) for p in Path("/renders").iterdir() if p.is_file()]
ingest_assets(sp, paths=files) Defensive patterns
Strategy: validation
Validate before calling
import stat
def is_regular_file(p) -> bool:
try:
return stat.S_ISREG(Path(p).stat().st_mode)
except OSError:
return False Type guard
def is_fingerprintable(p) -> bool:
from pathlib import Path
path = Path(p)
return path.exists() and path.is_file() and not path.is_symlink() Try / catch
try:
assets = ingest_assets(sp, paths=paths)
except ValueError as e:
if "Not a regular file" in str(e):
paths = [p for p in paths if Path(p).is_file()]
assets = ingest_assets(sp, paths=paths)
else:
raise Prevention
- Expand directories into individual files before ingestion.
- Never point asset lists at sockets, FIFOs, or device nodes.
- Write renderer output atomically to regular files before fingerprinting.
When it happens
Trigger: Calling _fingerprint (directly or via ingest_assets/validate_assets/_provenance/_bundle/_load) with a directory path, a named pipe, or a path that changes type between stat and open.
Common situations: Passing a directory instead of a file in an asset list; pointing at a Unix socket or log pipe; a renderer writing output as a FIFO; TOCTOU swap where the file is replaced mid-operation.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- artifact must be a resident regular file
- Invalid issue number
- exceeds the -byte limit
- must be a regular file
- media clips require a local source path
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/4304f864d9d48152.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/assets.py:48
raw = _text(str(value) if isinstance(value, Path) else value, 'path')
if '://' in raw or raw.startswith(('file:', 'http:', 'https:')):
raise ValueError('Only local filesystem paths are supported')
path = Path(raw).expanduser()
if not path.is_absolute():
path = base / path
# Check before resolving '..' to avoid hiding a symlink in the path.
for part in (path, *path.parents):
if part.is_symlink():
raise ValueError(f'Symlinks are not accepted: {part}')
return path.resolve()
def _fingerprint(path: Path, modality: str | None = None) -> dict[str, Any]:
_path(path, Path.cwd())
try:
before = path.stat()
if not stat.S_ISREG(before.st_mode):
raise ValueError(f'Not a regular file: {path}')
flags = os.O_RDONLY | getattr(os, 'O_NOFOLLOW', 0) | os.O_NONBLOCK
fd = os.open(path, flags)
with os.fdopen(fd, 'rb') as stream:
opened = os.fstat(stream.fileno())
if not stat.S_ISREG(opened.st_mode):
raise ValueError(f'Not a regular file: {path}')
digest = hashlib.sha256()
header = stream.read(12)
digest.update(header)
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
digest.update(chunk)
after = os.fstat(stream.fileno())
final = path.stat()
except OSError as exc:
raise ValueError(f'Cannot read local asset: {path}') from exc
def identity(info: os.stat_result) -> tuple[int, ...]:
return (info.st_dev, info.st_ino, info.st_size, info.st_mtime_ns, info.st_ctime_ns)
if len({identity(info) for info in (before, opened, after, final)}) != 1:View on GitHub (pinned to 8321021c54)