{"record":{"id":"206ee58d0cd600c2","repo":"plandex-ai/plandex","slug":"error-inserting-lockable-plan-id-v","errorCode":null,"errorMessage":"error inserting lockable plan id: %v","messagePattern":"error inserting lockable plan id: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/plan_helpers.go","lineNumber":65,"sourceCode":"\t\t\torgId,\n\t\t\tuserId,\n\t\t\tprojectId,\n\t\t\tname,\n\t\t\tplanConfig,\n\t\t).Scan(\n\t\t\t&plan.Id,\n\t\t\t&plan.CreatedAt,\n\t\t\t&plan.UpdatedAt,\n\t\t)\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error creating plan: %v\", err)\n\t\t}\n\n\t\t_, err = tx.Exec(\"INSERT INTO lockable_plan_ids (plan_id) VALUES ($1)\", plan.Id)\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error inserting lockable plan id: %v\", err)\n\t\t}\n\n\t\t// the one place where we do this to skip the locking queue\n\t\t// ok to cheat this once since we're creating a new plan\n\t\trepo := getGitRepo(orgId, plan.Id)\n\t\t_, err = CreateBranch(repo, plan, nil, \"main\", tx)\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error creating main branch: %v\", err)\n\t\t}\n\n\t\tlog.Println(\"Created branch main\")\n\n\t\terr = InitPlan(orgId, plan.Id)\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error initializing plan dir: %v\", err)\n\t\t}","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/plan_helpers.go#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\n_, err = tx.Exec(\"INSERT INTO lockable_plan_ids (plan_id) VALUES ($1)\", plan.Id)\nif err != nil {\n    return fmt.Errorf(\"error inserting lockable plan id: %v\", err)\n}\n// after (idempotent against stale rows)\n_, err = tx.Exec(\"INSERT INTO lockable_plan_ids (plan_id) VALUES ($1) ON CONFLICT (plan_id) DO NOTHING\", plan.Id)\nif err != nil {\n    return fmt.Errorf(\"error inserting lockable plan id: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// Go: confirm the lockable_plan_ids table exists before creating plans\nvar exists bool\nerr := Conn.Get(&exists, `SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'lockable_plan_ids')`)\nif err != nil || !exists {\n    return fmt.Errorf(\"lockable_plan_ids table missing; run migrations\")\n}","typeGuard":"func isUndefinedTable(err error) bool {\n    var pqErr *pq.Error\n    return errors.As(err, &pqErr) && pqErr.Code == \"42P01\"\n}","tryCatchPattern":"plan, err := db.CreatePlan(ctx, orgId, projectId, userId, name)\nif err != nil {\n    var pqErr *pq.Error\n    if errors.As(err, &pqErr) {\n        switch pqErr.Code {\n        case \"42P01\":\n            return nil, fmt.Errorf(\"schema out of date, run migrations: %w\", err)\n        case \"23505\":\n            return nil, fmt.Errorf(\"plan lock id already exists: %w\", err)\n        }\n    }\n    return nil, fmt.Errorf(\"create plan failed: %w\", err)\n}","preventionTips":["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"],"tags":["database","postgres","sql","transaction"],"backgroundTag":"sql-insert-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}