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

  1. Check the wrapped driver error for deadlock/timeout codes (40001/40P01) and retry cleanup
  2. Verify the ids slice is non-empty and well-formed before the query
  3. Keep the batch small (chunk ids) to avoid long-running deletes
  4. Ensure no other workers are contending on repo_locks during cleanup
  5. 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

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


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/0ff10c2869a28e48. Report an issue: GitHub.