plandex-ai/plandex · error
error inserting new lock: %v
Error message
error inserting new lock: %v
What it means
lockRepoDB wraps any failure of the INSERT INTO repo_locks ... ON CONFLICT DO NOTHING statement (other than the no-rows conflict case) into 'error inserting new lock'. It signals that the lock row could not be created in Postgres, so the plan-level repo lock was not acquired. The original database error is embedded in the message via %v.
Source
Thrown at app/server/db/locks.go:355
newLock.PlanBuildId,
newLock.Scope,
newLock.Branch,
).Scan(&insertedId)
if err != nil {
if err == sql.ErrNoRows {
// Means ON CONFLICT DO NOTHING prevented insertion
// => concurrency conflict => backoff & retry
return retryWithExponentialBackoff(params.Ctx,
errors.New("lock conflict: row not inserted"),
numRetry,
func(nextAttempt int) (string, error) {
return lockRepoDB(params, nextAttempt)
},
)
}
log.Printf("[Lock][%d] error inserting new lock: %v | reason: %s", goroutineID, err, params.Reason)
return "", fmt.Errorf("error inserting new lock: %v", err)
}
if insertedId.Valid {
newLock.Id = insertedId.String
} else {
if locksVerboseLogging {
log.Printf("no rows returned from insert query, means there was a conflict")
}
return retryWithExponentialBackoff(params.Ctx, err, numRetry, func(nextAttempt int) (string, error) {
return lockRepoDB(params, nextAttempt)
})
}
if locksVerboseLogging {
log.Printf("[Lock][%d] INSERT took %v | reason: %s",
goroutineID, time.Since(insertStart), params.Reason)
}
View on GitHub (pinned to e2d772072e)
Solutions
- Inspect the wrapped cause in the message (e.g. pq code 40001/40P01) — transient errors are retried by retryWithExponentialBackoff, so persistent ones usually mean a non-transient DB issue
- Verify the repo_locks schema matches the insert columns (org_id, user_id, plan_id, plan_build_id, scope, branch) and FK targets exist
- Check Postgres health: connection limits (max_connections), pool settings, and logs for deadlocks or cancellations at the same timestamp
- Ensure the caller's context deadline is generous enough for the insert under normal load
Example fix
// before
id, err := lockRepoDB(ctx, planId, orgId, scope)
if err != nil { log.Fatal(err) }
// after
id, err := lockRepoDB(ctx, planId, orgId, scope)
if err != nil {
var pqErr *pq.Error
if errors.As(err, &pqErr) && (pqErr.Code == "40001" || pqErr.Code == "40P01") {
// transient: rely on retry/backoff instead of failing hard
}
return fmt.Errorf("acquiring repo lock for plan %s: %w", planId, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if err := Conn.PingContext(ctx); err != nil {
return fmt.Errorf("db unavailable, skip lock acquisition: %w", err)
} Type guard
var pqErr *pq.Error
if errors.As(err, &pqErr) {
transient := pqErr.Code == "40001" || pqErr.Code == "40P01"
} Try / catch
id, err := lockRepoDB(ctx, orgId, planId, reason)
if err != nil {
var pqErr *pq.Error
if errors.As(err, &pqErr) && (pqErr.Code == "40001" || pqErr.Code == "40P01") {
// transient: safe to retry later
}
return fmt.Errorf("acquire lock plan %s: %w", planId, err)
} Prevention
- Ping the DB or check pool health before acquiring locks
- Give the acquisition context a deadline that covers retries
- Keep repo_locks schema/FK data (org_id, plan_id) valid before calling
- Monitor Postgres deadlock and serialization-failure logs
When it happens
Trigger: The INSERT returns a real DB error rather than sql.ErrNoRows: e.g. connection dropped mid-query, NOT NULL/FK violation on org_id, serialization failure (40001) or deadlock (40P01) under REPEATABLE READ, or context deadline exceeded on params.Ctx while the query runs.
Common situations: Postgres restarted or connection pool exhausted under load; concurrent plans deadlocking on the repo_locks unique index; invalid orgId referencing a missing orgs row; query canceled because the caller's context timed out before insert completed.
Related errors
- error adding plan context tokens: %v
- error starting transaction: %v
- error scanning repo lock: %v
- error iterating over repo locks: %v
- error removing expired locks: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/2daab962d66cd581.
Report an issue: GitHub.