Hmbown/CodeWhale · error · anyhow::Error

Automation lock must be a regular file

Error message

Automation lock must be a regular file

What it means

The automation manager opens its lock file with strict options and verifies via metadata that it is a regular file before wrapping it in an fd_lock RwLock. Directories, devices, FIFOs, or symlinks resolving to non-regular files are rejected to keep the lock semantics safe. (Note: because options.open follows symlinks, a symlink pointing at a regular file passes this check.)

Solutions

  1. Remove the non-regular file at the lock path (rm the directory/FIFO) and let the manager recreate it as a regular file
  2. Point the lock-file config at a correct, dedicated file path in a writable directory
  3. Check what is mounted at that path (mount/ls -l) if running in a container
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::metadata(&lock_path)?;
assert!(meta.is_file(), "automation lock path must be a regular file");

Try / catch

match result {
    Err(e) if e.to_string().contains("must be a regular file") => {
        std::fs::remove_file(&lock_path).ok(); // recreate lock
        // retry once
    }
    r => r?,
}

Prevention

When it happens

Trigger: The configured automation lock path exists but is a directory, a device node, a FIFO/socket, or otherwise not a regular file when the lock is opened.

Common situations: Lock path misconfigured to a directory; a stale mount or tmpfs artifact at the path; creating a FIFO/device at the expected location; container volume mounting a directory over the lock file path.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/automation_manager.rs:1023

        options.create(true).truncate(false).read(true).write(true);
        #[cfg(unix)]
        {
            use std::os::unix::fs::OpenOptionsExt as _;
            options
                .mode(0o600)
                .custom_flags(libc::O_NOFOLLOW | libc::O_CLOEXEC);
        }
        #[cfg(windows)]
        {
            use std::os::windows::fs::OpenOptionsExt as _;
            options.custom_flags(0x0020_0000); // FILE_FLAG_OPEN_REPARSE_POINT
        }
        let file = options
            .open(&path)
            .with_context(|| format!("open {}", path.display()))?;
        let metadata = file.metadata()?;
        if !metadata.is_file() {
            bail!("Automation lock must be a regular file");
        }
        #[cfg(unix)]
        {
            use std::os::unix::fs::MetadataExt as _;
            if metadata.nlink() != 1 {
                bail!("Automation lock must not have hard links");
            }
        }
        #[cfg(windows)]
        {
            use std::os::windows::fs::MetadataExt as _;
            if metadata.file_attributes() & 0x400 != 0 {
                bail!("Automation lock must not be a reparse point");
            }
        }
        Ok(fd_lock::RwLock::new(file))
    }

View on GitHub (pinned to 73e0f67d83)