{"record":{"id":"a2f39c75ef7843b5","repo":"gastownhall/beads","slug":"acquire-s-w","errorCode":null,"errorMessage":"acquire %s: %w","messagePattern":"acquire (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/server.go","lineNumber":115,"sourceCode":"\t\tport:        opts.Port,\n\t\tidleTimeout: opts.IdleTimeout,\n\t\tserver:      opts.Server,\n\t\tstats:       opts.Stats,\n\t\tstopEpoch:   opts.StopEpoch,\n\t}\n}\n\nfunc (p *proxyServer) tracef(format string, args ...any) {\n\tp.logger.Printf(format, args...)\n}\n\nfunc (p *proxyServer) ListenAndServe(parentCtx context.Context) error {\n\tlock, err := util.TryLock(filepath.Join(p.rootDir, LockFileName))\n\tif err != nil {\n\t\tif lockfile.IsLocked(err) {\n\t\t\treturn ErrLockHeld\n\t\t}\n\t\treturn fmt.Errorf(\"acquire %s: %w\", LockFileName, err)\n\t}\n\t// proxy.lock is held for the proxy's whole lifetime, but a doomed start\n\t// must be able to release it early (before its backend teardown) without\n\t// the deferred release double-unlocking. Only the main goroutine touches\n\t// this.\n\tlockHeld := true\n\treleaseLock := func() {\n\t\tif lockHeld {\n\t\t\tlockHeld = false\n\t\t\tlock.Unlock()\n\t\t}\n\t}\n\tdefer releaseLock()\n\tif err := clearSpawnMarkerAfterLock(p.rootDir); err != nil {\n\t\treturn fmt.Errorf(\"clear proxy spawn marker: %w\", err)\n\t}\n\n\t// Fast-abort: a concurrent `bd dolt stop` advances the stop epoch before","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/server.go#L97-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: second daemon against same root\nbd serve --root /data   // holds proxy.lock\nbd serve --root /data   // acquire error\n// after\nbd dolt stop --root /data && bd serve --root /data","handlingStrategy":"fallback","validationCode":"// before starting, check for a live holder\nif f, err := os.Open(filepath.Join(rootDir, \"proxy.lock\")); err == nil {\n  defer f.Close()\n  if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {\n    return fmt.Errorf(\"proxy already running for %s\", rootDir)\n  }\n}","typeGuard":null,"tryCatchPattern":"if err := p.ListenAndServe(ctx); err != nil {\n  if strings.Contains(err.Error(), \"acquire\") { /* another proxy holds the lock */ }\n  return err\n}","preventionTips":["Run exactly one proxy per rootDir","Use bd dolt stop before reusing a rootDir","Avoid NFS for rootDir; flock may be unreliable"],"tags":["file-lock","concurrency","startup"],"backgroundTag":"lock-acquire-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}