gastownhall/beads · error
acquiring lock (blocking): %w
Error message
acquiring lock (blocking): %w
What it means
AcquireSyncLock serializes concurrent `bd linear sync` runs per beads directory using an flock on `.linear-sync.lock`. When wait=true, it blocks until the lock is free; this error wraps a failure from that blocking acquisition. The lock file was opened successfully, but the blocking flock call itself failed (rather than merely being contended).
Source
Thrown at internal/linear/synclock.go:50
// If wait is true, blocks until the lock is available. If false, returns
// an error immediately when the lock is held by another live process.
func AcquireSyncLock(beadsDir string, wait bool) (*SyncLock, error) {
lockPath := filepath.Join(beadsDir, syncLockFilename)
infoPath := syncLockMetadataPath(beadsDir, lockPath)
if err := os.MkdirAll(beadsDir, 0755); err != nil {
return nil, fmt.Errorf("creating beads directory: %w", err)
}
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0600) // #nosec G304 -- lockPath is constrained to the beads directory.
if err != nil {
return nil, fmt.Errorf("opening lock file: %w", err)
}
if wait {
if err := lockfile.FlockExclusiveBlocking(f); err != nil {
_ = f.Close()
return nil, fmt.Errorf("acquiring lock (blocking): %w", err)
}
} else {
if err := lockfile.FlockExclusiveNonBlocking(f); err != nil {
if lockfile.IsLocked(err) || err == lockfile.ErrLockBusy {
info := readContendedSyncLockInfo(infoPath)
_ = f.Close()
return nil, &SyncLockHeldError{Info: info}
}
_ = f.Close()
return nil, fmt.Errorf("acquiring lock (non-blocking): %w", err)
}
}
metadata, err := publishSyncLockInfo(f, infoPath)
if err != nil {
_ = lockfile.FlockUnlock(f)
_ = f.Close()
return nil, fmt.Errorf("writing lock info: %w", err)View on GitHub (pinned to 71377f2769)
Solutions
- Retry AcquireSyncLock once or twice — transient failures like EINTR or I/O hiccups often clear
- Check that the beads directory lives on a local filesystem that supports flock (not NFS without lock daemon)
- Inspect the wrapped error (%w) to identify the underlying errno and address it (permissions, disk space, etc.)
- If another sync appears stuck, check the PID in the lock metadata (.linear-sync.lock info) and kill or wait for that process
Example fix
// before: ignoring retryable failure
lock, err := linear.AcquireSyncLock(beadsDir, true)
if err != nil { return err }
// after: retry transient blocking-acquire failures
var lock *linear.SyncLock
var err error
for i := 0; i < 3; i++ {
lock, err = linear.AcquireSyncLock(beadsDir, true)
if err == nil { break }
time.Sleep(100 * time.Millisecond)
}
if err != nil { return err } Defensive patterns
Strategy: retry
Validate before calling
// Pre-check: is another sync likely running (advisory)?
if info := linear.IsProcessAliveFromLockInfo(beadsDir); info != nil && linear.IsProcessAlive(info.PID) {
return fmt.Errorf("sync appears active (PID %d)", info.PID)
} Try / catch
lock, err := linear.AcquireSyncLock(beadsDir, true)
if err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) {
return fmt.Errorf("filesystem problem with %s: %w", pathErr.Path, err)
}
return fmt.Errorf("blocking lock acquire failed: %w", err)
}
defer lock.Release() Prevention
- Keep the beads directory on a local filesystem that supports flock
- Retry AcquireSyncLock a few times with small backoff for transient failures
- Avoid stopping the process with signals while it waits on the lock; handle SIGINT gracefully
- Monitor disk space and permissions on the beads directory
When it happens
Trigger: Calling AcquireSyncLock(beadsDir, true) when lockfile.FlockExclusiveBlocking returns an error that is not a normal busy result — e.g. EINTR after repeated signal interruptions, I/O error on the lock file, or a platform-level flock failure.
Common situations: Process receiving many signals during a long wait on a heavily contended lock; NFS/network filesystems where flock is unreliable; disk-full or permission-degraded filesystems; Windows file-share violations from antivirus or backup tools holding the lock file.
Related errors
- acquiring lock (non-blocking): %w
- ErrLockHeld
- lock busy: held by another process
- lock already held by another process
- server: ExternalDoltServer.Start: server already started
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ca5fed62ad5ebcbc.
Report an issue: GitHub.