Hmbown/CodeWhale · error
Automation lock must not have hard links
Error message
Automation lock must not have hard links
What it means
On Unix, after confirming the lock path is a regular file, the manager checks its hard-link count (st_nlink). A lock file with nlink != 1 means additional directory entries reference the same inode, which breaks the lock's uniqueness assumptions (another path could bypass or interfere with locking), so it bails.
Solutions
- Remove the extra hard links: find all links via `find -samefile <lockpath>` and delete the duplicates so nlink becomes 1
- Recreate the lock file: delete it and let the automation manager recreate it with a single link
- Configure backup/sync tools to copy instead of hard-link lock files
Example fix
// before $ ln /run/app/automation.lock /run/app/automation.lock.bak (nlink=2) // after $ rm /run/app/automation.lock.bak (nlink=1)
Defensive patterns
Strategy: validation
Validate before calling
#[cfg(unix)]
fn nlink_is_one(p: &std::path::Path) -> bool {
use std::os::unix::fs::MetadataExt;
std::fs::metadata(p).map(|m| m.nlink() == 1).unwrap_or(false)
} Try / catch
match acquire_automation_lock(path) {
Ok(g) => run(g),
Err(e) => {
eprintln!("Lock hard-linked: {e}; deleting lock and recreating");
let _ = std::fs::remove_file(path);
// retry acquisition once
}
} Prevention
- Never `ln` a lock file to another path; use symlinks absent or nothing at all
- Exclude lock files from backup/sync tools configured to hard-link
- Audit with `find -samefile <lockpath>` if you suspect duplicate links
When it happens
Trigger: Someone ran `ln <lockfile> <other-path>` creating a second hard link to the lock file, or a backup/sync tool hard-linked the lock.
Common situations: Dotfile managers (stow, rclone, backup restore) that hard-link files, users creating aliases to the lock file to 'share' state, or restores that re-link files.
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
- private sidecar file
- Automation lock must be a regular file
- config lock was redirected while opening
- could not securely open
- has multiple filesystem links, not a unique workspace-owned…
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/e200955f80186869.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/automation_manager.rs:1029
.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))
}
fn with_transaction<T>(&self, operation: impl FnOnce() -> Result<T>) -> Result<T> {
let mut lock = self.open_lock("state.lock")?;
let _guard = lock.write().context("lock automation state")?;
operation()
}
View on GitHub (pinned to 433685b202)