vercel/turborepo · error · std::io::Error
{path}: not a regular file
Error message
{path}: not a regular file What it means
When hashing workspace files for the cache key, the CRLF-aware blob hasher first validates the file type (crlf.rs:333): anything that is neither a regular file nor a directory — sockets, FIFOs, char/block devices — is rejected with InvalidInput '{path}: not a regular file'. Directories deliberately pass through to fail later with a descriptive IsADirectory error; this guard targets exotic node types that would otherwise hash garbage or hang on a FIFO.
Source
Thrown at crates/turborepo-scm/src/crlf.rs:333
}
/// Hash a byte slice as a git blob. Used to verify symlink entries, whose
/// blob content is the link target path (no filters ever apply).
pub(crate) fn hash_bytes_as_blob(bytes: &[u8]) -> Result<OidHash, std::io::Error> {
let mut hasher = BlobHasher::new();
hasher.write_blob_header(bytes.len() as u64);
hasher.update(bytes);
hasher.finalize()
}
fn validate_file_type(
path: &AbsoluteSystemPath,
metadata: &std::fs::Metadata,
) -> Result<(), std::io::Error> {
// Reject exotic file types (sockets, FIFOs, device nodes). Directories
// pass through to fail naturally with a descriptive IsADirectory error.
if !metadata.is_file() && !metadata.is_dir() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("{path}: not a regular file"),
));
}
Ok(())
}
/// Hash a file as a git blob, applying CRLF→LF normalization when the
/// `text` attribute requires it.
///
/// For `TextAttr::Auto`, binary detection (NUL in first 8KB) is performed
/// during the fused scan+hash pass — no separate file open.
///
/// Single-pass for the common case (no normalization needed):
/// 1. Get file length from metadata
/// 2. Fused scan + speculative raw hash: write blob header, then scan for CRLFs
/// while simultaneously feeding raw bytes into the hasher
/// 3. If no normalization needed, return the raw hash (one read total)View on GitHub (pinned to 7fe373bc27)
Solutions
- Find the offending entry: the message names the exact path; `file <path>` confirms the type
- Delete it or move the tool that creates it outside the workspace
- Tighten turbo.json `inputs` globs to specific extensions/dirs so sockets and FIFOs are excluded
Example fix
// before (turbo.json) "inputs": ["**/*"] // after — exclude socket/fifo artifacts "inputs": ["src/**/*", "!**/*.sock", "!run/**"]
Defensive patterns
Strategy: type-guard
Validate before calling
// exclude non-regular files before hashing inputs
for entry in walkdir::WalkDir::new(dir) {
let e = entry?;
let m = e.metadata()?;
if !(m.is_file() || m.is_dir()) { continue; } // skip sockets, fifos, devices
hash_file(e.path())?;
} Type guard
fn is_hashable(m: &std::fs::Metadata) -> bool {
// uses symlink-free metadata: mirrors validate_file_type in crlf.rs
m.is_file() || m.is_dir()
} Try / catch
match hash_path(path) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput
&& e.to_string().contains("not a regular file") => { skipped.push(path); continue; }
r => r?,
} Prevention
- Keep *.sock / FIFO artifacts out of workspace dirs hashed by turbo
- Scope turbo.json `inputs` to the files tasks actually read
- Add pre-commit checks for stray socket files in the repo
When it happens
Trigger: A glob/task input in turbo.json matching a Unix socket or named pipe inside the workspace — e.g. a dev server dropped a *.sock file, or a tools directory contains an mkfifo artifact that a broad `inputs` pattern picks up.
Common situations: dev/.sock or *.socket files created by local daemons inside the repo FIFOs from ad-hoc scripts left in the tree Repo unpacked over a path containing device nodes (rare)
Related errors
- path component contains NUL byte: {component:?}
- daemon socket parent is not a directory: {socket_dir}
- CRLF normalization length mismatch: scan predicted {normaliz
- Unable to write .gitignore
- Unable to write package.json
AI-assisted analysis of vercel/turborepo@7fe373bc27 (2026-08-17).
Data as JSON: /api/errors/d8f6b7c9fd3ae7f1.
Report an issue: GitHub.