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
- Remove the non-regular entry at the lock path and let the application recreate a regular lock file
- Verify the state directory contents (ls -la) and restore a regular file/empty path
- Recreate the state directory from scratch if its contents are untrusted
- 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
- Never redirect the lock path to devices/fifos (e.g. /dev/null) in wrappers or sandboxes
- Audit state directory contents after restores or migrations
- Recreate the state directory rather than hand-crafting lock entries
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
- Automation lock must not be a reparse point
- Automation lock must not have hard links
- refusing a symlinked Runtime Chat owner lock
- Automation lock must be a regular file
- built-in plugin path may not be a symbolic link or reparse…
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)