gastownhall/beads · error

writing lock info: %w

Error message

writing lock info: %w

What it means

After acquiring the exclusive flock, AcquireSyncLock publishes diagnostic metadata (PID, start time) to the lock info file via publishSyncLockInfo. If that write fails, the kernel lock is released and this error is returned. The lock is NOT left held, so the sync did not start.

Source

Thrown at internal/linear/synclock.go:68

			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)
	}

	return &SyncLock{infoPath: infoPath, file: f, metadata: metadata}, nil
}

// Release releases the sync lock. The kernel-lock file is NOT removed — doing
// so after unlocking creates a race where a blocked waiter acquires the old
// inode while a new process creates a fresh file at the same path, splitting
// lock identity. On Unix, inline owner metadata is truncated while still
// holding the lock. On Windows, a separate advisory record is cleared while
// still holding the authoritative guard.
func (l *SyncLock) Release() error {
	if l == nil || l.file == nil {
		return nil
	}

	// Clear or invalidate diagnostic metadata before releasing the authoritative
	// lock. Platform helpers preserve Unix errors and make Windows diagnostics

View on GitHub (pinned to 71377f2769)

Solutions

  1. Free disk space / raise quota on the volume holding the beads directory
  2. Verify the beads directory and lock files are writable by the current user
  3. Check nothing else deletes or chmods files inside the beads directory during sync
  4. Retry after fixing the filesystem condition — the flock was properly released on failure, so it is safe to retry
Defensive patterns

Strategy: validation

Validate before calling

// Before acquiring: ensure the beads dir is writable and has free space
info, err := os.Stat(beadsDir)
if err != nil || !info.IsDir() { return fmt.Errorf("bad beads dir: %w", err) }
probe := filepath.Join(beadsDir, ".write-probe")
if err := os.WriteFile(probe, []byte("x"), 0600); err != nil {
    return fmt.Errorf("beads dir not writable: %w", err)
}
os.Remove(probe)

Try / catch

lock, err := linear.AcquireSyncLock(beadsDir, true)
if err != nil && strings.Contains(err.Error(), "writing lock info") {
    return fmt.Errorf("cannot persist lock metadata (check disk space/permissions): %w", err)
}

Prevention

When it happens

Trigger: Calling AcquireSyncLock when publishSyncLockInfo → writeLockInfo fails to truncate/seek/write the lock-info file — typically disk full, read-only filesystem, or the info path becoming unwritable.

Common situations: Disk-quota exhaustion on the volume holding the beads directory; beads dir on a read-only mount; another tool deleting or chmod-ing the lock metadata path between open and write; Windows handle contention on the info file.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/7f582a17916b4a41. Report an issue: GitHub.