plandex-ai/plandex · error
error removing all locks: %v
Error message
error removing all locks: %v
What it means
Inside CleanupActiveLocks, the batch DELETE of all active lock rows (using pq.Array(ids)) failed with an error other than sql.ErrNoRows; it is wrapped as 'error removing all locks'. The transaction aborts before commit, so stale locks remain in repo_locks.
Source
Thrown at app/server/db/locks.go:653
} else {
if locksVerboseLogging {
log.Println("transaction rolled back")
}
}
}()
// Delete all active locks
query := "DELETE FROM repo_locks WHERE id = ANY($1)"
ids := make([]string, 0, len(activeLockIds))
for id := range activeLockIds {
ids = append(ids, id)
}
_, err = tx.Exec(query, pq.Array(ids))
if err != nil {
if err == sql.ErrNoRows {
log.Println("No active locks to cleanup")
} else {
return fmt.Errorf("error removing all locks: %v", err)
}
}
// Commit the transaction
if err = tx.Commit(); err != nil {
return fmt.Errorf("error committing transaction: %v", err)
}
activeLockIdsMu.Lock()
activeLockIds = make(map[string]bool)
activeLockIdsMu.Unlock()
log.Println("Successfully cleaned up all repo locks")
return nil
}
View on GitHub (pinned to e2d772072e)
Solutions
- Check the wrapped driver error for deadlock/timeout codes (40001/40P01) and retry cleanup
- Verify the ids slice is non-empty and well-formed before the query
- Keep the batch small (chunk ids) to avoid long-running deletes
- Ensure no other workers are contending on repo_locks during cleanup
- Confirm pq (lib/pq) import and driver version match the array-parameter usage
Example fix
// before
_, err = tx.Exec(query, pq.Array(ids))
if err != nil { return err }
// after
_, err = tx.Exec(query, pq.Array(ids))
if err != nil {
if isDeadlockError(err) {
return CleanupActiveLocks(ctx) // safe: tx rolled back, retry whole cleanup
}
return fmt.Errorf("cleanup: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
if len(ids) == 0 {
return nil // nothing to clean
}
if err := Conn.PingContext(ctx); err != nil {
return fmt.Errorf("db unavailable: %w", err)
} Type guard
func isDeleteAllFailure(err error) bool {
return err != nil && !errors.Is(err, sql.ErrNoRows) && strings.Contains(err.Error(), "error removing all locks")
} Try / catch
if err := CleanupActiveLocks(ctx); err != nil {
if isDeleteAllFailure(err) {
time.Sleep(time.Second)
return CleanupActiveLocks(ctx) // tx rolled back; full retry is safe
}
return err
} Prevention
- Chunk large id lists to keep deletes short
- Resolve contention — don't run cleanup while workers actively lock/unlock
- Validate pq.Array usage and driver compatibility
- Retry whole cleanup on failure since the tx aborts atomically
When it happens
Trigger: The DELETE ... WHERE id = ANY($1) statement errors: connection lost mid-statement, syntax/type mismatch on the array parameter, deadlock with a concurrent lock operation, or statement timeout on a huge id list.
Common situations: Very large activeLockIds map producing a slow delete; pq.Array misuse after driver upgrade; deadlock because other workers hold row locks on repo_locks during shutdown; DB failover mid-cleanup.
Related errors
- error deleting draft plans: %v
- error deleting plans: %v
- Error deleting plan:
- error adding plan context tokens: %v
- error deleting invite: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/0ff10c2869a28e48.
Report an issue: GitHub.