plandex-ai/plandex · error
error inserting lockable plan id: %v
Error message
error inserting lockable plan id: %v
What it means
This error wraps a failure of the INSERT INTO lockable_plan_ids (plan_id) VALUES ($1) statement inside CreatePlan (app/server/db/plan_helpers.go:65). Every plan must have a corresponding row in lockable_plan_ids so the plan-locking system can acquire locks on it. The plans row was already inserted successfully in the same transaction, so this failure rolls back the whole plan creation.
Source
Thrown at app/server/db/plan_helpers.go:65
orgId,
userId,
projectId,
name,
planConfig,
).Scan(
&plan.Id,
&plan.CreatedAt,
&plan.UpdatedAt,
)
if err != nil {
return fmt.Errorf("error creating plan: %v", err)
}
_, err = tx.Exec("INSERT INTO lockable_plan_ids (plan_id) VALUES ($1)", plan.Id)
if err != nil {
return fmt.Errorf("error inserting lockable plan id: %v", err)
}
// the one place where we do this to skip the locking queue
// ok to cheat this once since we're creating a new plan
repo := getGitRepo(orgId, plan.Id)
_, err = CreateBranch(repo, plan, nil, "main", tx)
if err != nil {
return fmt.Errorf("error creating main branch: %v", err)
}
log.Println("Created branch main")
err = InitPlan(orgId, plan.Id)
if err != nil {
return fmt.Errorf("error initializing plan dir: %v", err)
}View on GitHub (pinned to e2d772072e)
Solutions
- Read the wrapped %v error for the pq error code; if it's 42P01 (undefined_table), run the migrations that create lockable_plan_ids
- If it's a unique_violation, check for orphaned lockable_plan_ids rows or a trigger re-inserting the id and clean up
- Verify DB connectivity and transaction timeouts; retry CreatePlan if the failure was transient
- Confirm plan.Id was returned correctly by the prior RETURNING insert (an empty id causes FK/duplicate problems)
Example fix
// before
_, err = tx.Exec("INSERT INTO lockable_plan_ids (plan_id) VALUES ($1)", plan.Id)
if err != nil {
return fmt.Errorf("error inserting lockable plan id: %v", err)
}
// after (idempotent against stale rows)
_, err = tx.Exec("INSERT INTO lockable_plan_ids (plan_id) VALUES ($1) ON CONFLICT (plan_id) DO NOTHING", plan.Id)
if err != nil {
return fmt.Errorf("error inserting lockable plan id: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: confirm the lockable_plan_ids table exists before creating plans
var exists bool
err := Conn.Get(&exists, `SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'lockable_plan_ids')`)
if err != nil || !exists {
return fmt.Errorf("lockable_plan_ids table missing; run migrations")
} Type guard
func isUndefinedTable(err error) bool {
var pqErr *pq.Error
return errors.As(err, &pqErr) && pqErr.Code == "42P01"
} Try / catch
plan, err := db.CreatePlan(ctx, orgId, projectId, userId, name)
if err != nil {
var pqErr *pq.Error
if errors.As(err, &pqErr) {
switch pqErr.Code {
case "42P01":
return nil, fmt.Errorf("schema out of date, run migrations: %w", err)
case "23505":
return nil, fmt.Errorf("plan lock id already exists: %w", err)
}
}
return nil, fmt.Errorf("create plan failed: %w", err)
} Prevention
- Run all migrations on deploy so lockable_plan_ids exists before use
- Make the lockable_plan_ids insert idempotent (ON CONFLICT DO NOTHING)
- Clean up orphaned lockable_plan_ids rows after failed creations
- Watch transaction/connection errors under load and retry plan creation
When it happens
Trigger: Calling CreatePlan when the lockable_plan_ids table is missing or has a different schema, when a row for the same plan_id already exists (duplicate key on the primary key), when the FK from lockable_plan_ids.plan_id to plans.id is violated by a trigger/deferred constraint, or when the DB connection drops mid-transaction.
Common situations: Schema drift after upgrading the server without running migrations (lockable_plan_ids table absent), stale data from a previous partial migration, or connection-pool/timeout issues under heavy plan-creation load.
Related errors
- error creating plan: %v
- error updating user num_non_draft_plans: %v
- error creating invite: %v
- error deleting invite: %v
- error accepting invite: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/206ee58d0cd600c2.
Report an issue: GitHub.