plandex-ai/plandex · error
failed to get DB lock: %w
Error message
failed to get DB lock: %w
What it means
The queue acquires a DB-backed lock before processing batched operations. When lock acquisition fails, each queued operation's done channel receives `failed to get DB lock: %w` so every waiting caller learns the batch could not proceed; the queue then returns to process the rest of the queue rather than blocking.
Source
Thrown at app/server/db/queue.go:210
defer func() {
log.Printf("[Queue] Releasing DB lock %s for plan %s", lockId, firstOp.planId)
releaseErr := deleteRepoLockDB(lockId, firstOp.planId, firstOp.reason, 0)
if releaseErr != nil {
log.Printf("[Queue] Failed to release DB lock: %v", releaseErr)
} else {
log.Printf("[Queue] DB lock %s released successfully", lockId)
}
}()
}
if err != nil {
log.Printf("[Queue] Failed to get DB lock: %v", err)
for _, op := range ops {
if locksVerboseLogging {
log.Printf("[Queue] Notifying operation %s (%s) of lock failure", op.id, op.reason)
}
op.done <- fmt.Errorf("failed to get DB lock: %w", err)
}
// we still need to process the rest of the queue
// if the error is critical, caller will handle it
return
}
if locksVerboseLogging {
log.Printf("[Queue] Acquired DB lock %s, processing batch of %d operations", lockId, len(ops))
}
repo := getGitRepo(firstOp.orgId, firstOp.planId)
var needsRollback bool
// Process the batch
// If it's a writer => single op
// If multiple same‐branch readers => do them in parallel
var wg sync.WaitGroup
for _, op := range ops {View on GitHub (pinned to e2d772072e)
Solutions
- Inspect the wrapped err logged as '[Queue] Failed to get DB lock' for the root cause
- Check which transaction holds the DB lock (pg_locks / blocking queries) and shorten it
- Increase the lock acquisition timeout or add bounded retry with backoff
- Verify DB connectivity and pool sizing under load
Defensive patterns
Strategy: retry
Validate before calling
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("database unavailable before queuing ops: %w", err)
} Try / catch
done := make(chan error, 1)
queueOp(op, done)
select {
case err := <-done:
if err != nil && strings.Contains(err.Error(), "failed to get DB lock") {
// bounded retry with backoff
time.Sleep(backoff)
queueOp(op, done)
}
case <-ctx.Done():
return ctx.Err()
} Prevention
- Keep transactions that hold the DB lock short
- Enable locksVerboseLogging to diagnose contention
- Size the connection pool for lock acquisition queries
- Use context timeouts on ops waiting on the queue
When it happens
Trigger: The DB lock query/transaction used by the queue fails at queue.go:210 — DB unreachable, lock contention timeout, or an aborted lock transaction — while a batch of queued ops is pending.
Common situations: Long-running write transactions holding the lock past the timeout, database failover/restart, connection pool exhaustion preventing the lock acquisition query from running.
Related errors
- panic in SyncPlanTokens: %v %s
- error getting contexts or convo: %v
- panic in operation: %v %s
- error storing convo message description: %v
- error storing description: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/cedff55108bbfaed.
Report an issue: GitHub.