affaan-m/ECC · error

{label} must be a regular file

Error message

{label} must be a regular file

What it means

Runtime error from a bounded-file reader in ecc2/src/main.rs. After opening `path` (with O_NONBLOCK on unix) and reading metadata, the code checks metadata.is_file(); if the path is not a regular file it bails with "{label} must be a regular file", where `label` names the input (e.g. a flag like --patch or --input). The check excludes directories, devices, sockets, pipes, and non-regular specials, so the input cannot be streamed or stat'd as bytes.

Source

Thrown at ecc2/src/main.rs:1409

    details: BTreeMap<String, String>,
}

fn read_bounded_file(path: &Path, max_bytes: u64, label: &str) -> Result<Vec<u8>> {
    let mut options = File::options();
    options.read(true);
    #[cfg(unix)]
    {
        use std::os::unix::fs::OpenOptionsExt;
        options.custom_flags(libc::O_NONBLOCK);
    }
    let file = options
        .open(path)
        .with_context(|| format!("Failed to open {}", path.display()))?;
    let metadata = file
        .metadata()
        .with_context(|| format!("Failed to inspect {}", path.display()))?;
    if !metadata.is_file() {
        anyhow::bail!("{label} must be a regular file");
    }

    let read_limit = max_bytes
        .checked_add(1)
        .context("bounded input byte limit is too large")?;
    let mut content = Vec::new();
    file.take(read_limit)
        .read_to_end(&mut content)
        .with_context(|| format!("Failed to read {}", path.display()))?;
    if content.len() as u64 > max_bytes {
        anyhow::bail!("{label} exceeds the {max_bytes}-byte limit");
    }
    Ok(content)
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Point the flag at an actual file path; verify with ls -l / stat.
  2. If you intended stdin, check whether the command supports - as a sentinel; otherwise write to a temp file first.
  3. Resolve symlinks (readlink -f) to confirm the target is a regular file.
  4. Avoid passing device nodes or pipes to flags that require regular files.

Example fix

# before
$ ecc cmd --patch ./patches/

# after
$ ecc cmd --patch ./patches/0001-fix.patch
Defensive patterns

Strategy: validation

Validate before calling

// Verify regular-file-ness before invoking the bounded reader.
use std::fs;
fn assert_regular_file(label: &str, path: &Path) -> Result<()> {
    let meta = fs::symlink_metadata(path)?;
    if meta.file_type().is_symlink() {
        let target = fs::metadata(path)?;
        anyhow::ensure!(target.is_file(), "{label} must be a regular file");
    } else {
        anyhow::ensure!(meta.is_file(), "{label} must be a regular file");
    }
    Ok(())
}

Type guard

fn is_regular_file(path: &Path) -> bool {
    fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)
}

Try / catch

let content = read_bounded(label, path, max_bytes).map_err(|e| {
    if e.to_string().contains("must be a regular file") {
        anyhow::anyhow!("{e}; pass a file path, not a directory or device")
    } else {
        e
    }
})?;

Prevention

When it happens

Trigger: Passing a path to a bounded-input flag where the path resolves to a directory, FIFO, device, socket, or symlink whose target is not a regular file. The open succeeds but metadata().is_file() returns false.

Common situations: User passed a directory path by mistake; a symlink points at a directory; /dev/null or /dev/stdin used where a regular file is required; a named pipe created by another process; a glob expanded to a directory.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/9c7fdd8e8a8c66ad. Report an issue: GitHub.