gastownhall/beads · error
acquire %s: %w
Error message
acquire %s: %w
What it means
ListenAndServe first acquires proxy.lock under rootDir; failure to create or lock the file is wrapped as 'acquire %s: %w'. The lock serializes the whole proxy lifetime, so without it the proxy must not start. A doomed start may release the lock early, but only the main goroutine touches it.
Source
Thrown at internal/storage/dbproxy/proxy/server.go:115
port: opts.Port,
idleTimeout: opts.IdleTimeout,
server: opts.Server,
stats: opts.Stats,
stopEpoch: opts.StopEpoch,
}
}
func (p *proxyServer) tracef(format string, args ...any) {
p.logger.Printf(format, args...)
}
func (p *proxyServer) ListenAndServe(parentCtx context.Context) error {
lock, err := util.TryLock(filepath.Join(p.rootDir, LockFileName))
if err != nil {
if lockfile.IsLocked(err) {
return ErrLockHeld
}
return fmt.Errorf("acquire %s: %w", LockFileName, err)
}
// proxy.lock is held for the proxy's whole lifetime, but a doomed start
// must be able to release it early (before its backend teardown) without
// the deferred release double-unlocking. Only the main goroutine touches
// this.
lockHeld := true
releaseLock := func() {
if lockHeld {
lockHeld = false
lock.Unlock()
}
}
defer releaseLock()
if err := clearSpawnMarkerAfterLock(p.rootDir); err != nil {
return fmt.Errorf("clear proxy spawn marker: %w", err)
}
// Fast-abort: a concurrent `bd dolt stop` advances the stop epoch beforeView on GitHub (pinned to 71377f2769)
Solutions
- Stop the other proxy process using the same rootDir (bd dolt stop or kill it)
- Remove a stale lock only after confirming no live holder
- Fix rootDir permissions so the lock file can be created/locked
Example fix
// before: second daemon against same root bd serve --root /data // holds proxy.lock bd serve --root /data // acquire error // after bd dolt stop --root /data && bd serve --root /data
Defensive patterns
Strategy: fallback
Validate before calling
// before starting, check for a live holder
if f, err := os.Open(filepath.Join(rootDir, "proxy.lock")); err == nil {
defer f.Close()
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
return fmt.Errorf("proxy already running for %s", rootDir)
}
} Try / catch
if err := p.ListenAndServe(ctx); err != nil {
if strings.Contains(err.Error(), "acquire") { /* another proxy holds the lock */ }
return err
} Prevention
- Run exactly one proxy per rootDir
- Use bd dolt stop before reusing a rootDir
- Avoid NFS for rootDir; flock may be unreliable
When it happens
Trigger: Calling proxyServer.ListenAndServe when proxy.lock cannot be created/locked in rootDir — another proxy holds the lock, or the lock file can't be opened/created.
Common situations: Two `bd` daemons started against the same rootDir; leftover lock after a crashed start (usually auto-released by flock semantics); read-only or permission-restricted rootDir; NFS mounts without flock support.
Related errors
- lock busy: held by another process
- lock already held by another process
- errStartInterrupted
- ErrLockHeld
- acquiring lock (blocking): %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a2f39c75ef7843b5.
Report an issue: GitHub.