Hmbown/CodeWhale · error · io::Error

Fleet file must be a regular, non-hard-linked file

Error message

Fleet file must be a regular, non-hard-linked file

What it means

This error means the opened Fleet artifact is not a regular file or has more than one hard link, either of which could allow another directory entry to mutate the same inode concurrently. The library throws it from open_with_flags right after creating/opening the fd, guarding the Unix confined-I/O path.

Solutions

  1. Find and remove the extra hard link (find dir -samefile <artifact>) so nlink returns to 1
  2. Delete and let Codewhale recreate the artifact file
  3. Ensure the path refers to a regular file, not a directory or device node

Example fix

// before
ln existing.json artifacts/dup.json
// after
rm artifacts/dup.json  # keep a single link
Defensive patterns

Strategy: validation

Validate before calling

use std::os::unix::fs::MetadataExt;
let md = std::fs::metadata(path)?;
if !md.is_file() || md.nlink() != 1 {
    return Err("artifact is not a singly-linked regular file");
}

Try / catch

match open_update(dir, name) {
    Ok(f) => edit(f),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        eprintln!("artifact linked or not regular; recreate it");
        std::fs::remove_file(dir.join(name)).ok();
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling open_update or open_file when the target is a directory/FIFO/device, or when a second hard link exists (st_nlink > 1) pointing at the same inode.

Common situations: Something hard-linked an artifact into another directory; the workspace path points at a special file; a filesystem supporting hard links (ext4, not most network FS) has a duplicate entry created by a backup or dedupe tool.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/c86595f7df08923c. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/files.rs:134

        use std::os::fd::{AsRawFd, FromRawFd};
        use std::os::unix::fs::MetadataExt;
        // SAFETY: a pinned parent and validated basename; never follows links.
        let fd = unsafe {
            libc::openat(
                self.directory.as_raw_fd(),
                self.filename.as_ptr(),
                flags | libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK,
                0o600,
            )
        };
        if fd < 0 {
            return Err(io::Error::last_os_error());
        }
        // SAFETY: fd is freshly owned.
        let file = unsafe { File::from_raw_fd(fd) };
        let metadata = file.metadata()?;
        if !metadata.is_file() || metadata.nlink() != 1 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "Fleet file must be a regular, non-hard-linked file",
            ));
        }
        Ok(file)
    }

    pub(crate) fn publish(&self, bytes: &[u8]) -> io::Result<()> {
        self.atomic_write(bytes, false)
    }

    pub(crate) fn replace(&self, bytes: &[u8]) -> io::Result<()> {
        self.atomic_write(bytes, true)
    }

    fn atomic_write(&self, bytes: &[u8], replace: bool) -> io::Result<()> {
        use std::os::fd::{AsRawFd, FromRawFd};
        let temporary =

View on GitHub (pinned to 73e0f67d83)