Hmbown/CodeWhale · error

Runtime Chat owner lock is not a regular file

Error message

Runtime Chat owner lock is not a regular file

What it means

After opening the Runtime Chat owner lock, RelayScopeLock::acquire verifies via file metadata that the opened path is actually a regular file; if not (fifo, device, socket, directory), it bails. This prevents reading/writing or chmod-ing special files that happen to sit at the lock path.

Solutions

  1. Remove the non-regular entry at the lock path and let the application recreate a regular lock file
  2. Verify the state directory contents (ls -la) and restore a regular file/empty path
  3. Recreate the state directory from scratch if its contents are untrusted
  4. Investigate the process or tool that placed the special file there

Example fix

// before
mkfifo ~/.local/state/codewhale/runtime-chat-owner.lock
// after
rm ~/.local/state/codewhale/runtime-chat-owner.lock  # recreated as a regular file on next acquire
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

match RelayScopeLock::acquire(&lock_path) {
    Err(e) if e.to_string().contains("not a regular file") => {
        eprintln!("lock path is not a regular file; recreating state directory");
        std::fs::remove_file(&lock_path)?;
        RelayScopeLock::acquire(&lock_path)?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Acquiring the owner scope lock when the lock path exists but is not a regular file — e.g. a named pipe, socket, or device node was placed at the path, or the open raced with a replacement between the symlink check and open.

Common situations: Tampering or misconfigured state directories; someone pointing the lock path at /dev/null or a fifo; corrupted setups where a directory occupies the expected file path.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/runtime_chat_relay.rs:1586

        #[cfg(unix)]
        {
            use std::os::unix::fs::OpenOptionsExt as _;
            options.mode(0o600).custom_flags(libc::O_NOFOLLOW);
        }
        #[cfg(windows)]
        {
            use std::os::windows::fs::OpenOptionsExt as _;
            use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT;
            options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);
        }
        let file = options.open(path).context("open Runtime Chat owner lock")?;
        if !file
            .metadata()
            .context("inspect Runtime Chat owner lock")?
            .file_type()
            .is_file()
        {
            bail!("Runtime Chat owner lock is not a regular file");
        }
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt as _;
            file.set_permissions(fs::Permissions::from_mode(0o600))
                .context("protect Runtime Chat owner lock")?;
        }
        // Same-process drop-then-reopen can observe WouldBlock for a brief
        // window while the previous fd is still closing (#5735). Retry only
        // that contention; a lock that stays held is still ownership.
        let deadline = Instant::now() + Duration::from_millis(25);
        loop {
            match Self::try_lock_exclusive(&file) {
                Ok(()) => break,
                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
                    if Instant::now() >= deadline {
                        return Err(error).context("acquire Runtime Chat owner lock");
                    }

View on GitHub (pinned to 73e0f67d83)