EpicGames/lore · error · io::Error (InvalidInput)
not a regular file
Error message
not a regular file: {} What it means
lore-storage's chunker `open_read` opens a path for reading and then verifies via the file's own metadata that it is a regular file. Directories, FIFOs, sockets, and device files are rejected with `ErrorKind::InvalidInput` and the offending path in the message. This prevents the chunker from streaming a directory handle or a special file and producing bogus chunk data.
Solutions
- Pass a regular file path instead of the directory/special file named in the message.
- Pre-check `metadata(path)?.is_file()` before calling `open_read` and skip non-files.
- If the path should be a directory, use the library's directory-walking API rather than opening it directly.
Example fix
// before
let (file, len) = chunker::open_read(user_path).await?;
// after
if !tokio::fs::metadata(user_path).await?.is_file() {
eprintln!("skipping non-file: {}", user_path.display());
return Ok(());
}
let (file, len) = chunker::open_read(user_path).await?; Defensive patterns
Strategy: validation
Validate before calling
let meta = tokio::fs::metadata(path).await?;
if !meta.is_file() {
anyhow::bail!("not a regular file: {}", path.display());
} Try / catch
match chunker::open_read(path).await {
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
eprintln!("skipping non-regular file: {}", path.display());
Ok(())
}
other => other.map(|_| ()),
} Prevention
- Filter directory walks to regular files before chunking.
- Reject directories and special files early at the CLI/CLI-input boundary.
- Resolve symlinks and re-check the target kind before opening.
When it happens
Trigger: Passing a directory or special file (fifo/socket/dev node) to `open_read`, `open_chunker`, or `streamed_chunks` — typically a user-supplied path that was never checked to be a file.
Common situations: Users pointing a tool at a directory instead of a file, a path that is a symlink to a directory, glob patterns that matched directories, or named pipes created by other tooling.
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
- file refused further writes
- file ended before the requested read length
- file refused further writes
- file shrank while reading
- level header file has invalid magic 0x
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/484422e5129de998.
Report an issue: GitHub.
Appendix: source
Thrown at lore-storage/src/chunker.rs:69
pub struct Chunk {
pub offset: u64,
pub data: Bytes,
}
/// Open `path` for reading, returning the shared handle and its size.
///
/// The size comes off the open handle rather than the path, so it describes the bytes about to be
/// read rather than what a separate stat of the path once saw. The same stat carries the file type,
/// so refusing anything but a regular file costs nothing beyond it — and has to happen here:
/// opening a directory read-only succeeds, and the size it reports is whatever the filesystem
/// chooses.
pub async fn open_read(path: &Path) -> std::io::Result<(IoFile, u64)> {
let file = IoDriver::global()
.open(path, &OpenOptions::new().read(true))
.await?;
let metadata = file.metadata().await?;
if !metadata.is_file() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("not a regular file: {}", path.display()),
));
}
Ok((file, metadata.len()))
}
/// How the chunker picks cut points.
enum CutMode {
/// Cut where the content says to, matching whole-file `FastCDC`.
ContentDefined,
/// Cut every N bytes. Never exceeds [`FRAGMENT_SIZE_THRESHOLD`], so the window
/// always holds at least one whole chunk.
FixedSize(usize),
}
/// The window a read fills, owned by the operation for its whole flight and handed back
/// with it. A single segment: the read lands in `buffer[start..start + want]`, leavingView on GitHub (pinned to 074eb0b0d1)