{"record":{"id":"f3066b7b4e717b94","repo":"plandex-ai/plandex","slug":"error-creating-plan-v-f3066b","errorCode":null,"errorMessage":"error creating plan: %v","messagePattern":"error creating plan: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/plan_helpers.go","lineNumber":59,"sourceCode":"\t\t\tName:       name,\n\t\t\tPlanConfig: planConfig,\n\t\t}\n\n\t\terr = tx.QueryRow(\n\t\t\tquery,\n\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\")","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/plan_helpers.go#L41-L77","documentation":"This error wraps any failure from the INSERT into the 'plans' table inside CreatePlan (app/server/db/plan_helpers.go:59). The insert uses tx.QueryRow(...).Scan(...) with a RETURNING clause, so it fails when the INSERT itself errors or when scanning the returned id/created_at/updated_at fails. Because it runs inside a WithTx transaction, a failure here aborts the whole plan-creation transaction.","triggerScenarios":"Calling CreatePlan when the plans insert violates a constraint (e.g. duplicate plan name if unique, invalid org_id/user_id/project_id foreign keys), when the database connection is down, when plan_config serialization is incompatible with the column type, or when Scan cannot decode the RETURNING columns (schema/type mismatch between the Plan struct and the table).","commonSituations":"Database migrations out of sync with the deployed code (missing or altered plans columns), Postgres connection pool exhaustion or transient connection drops, FK violations from a project or user that was deleted concurrently, or running against an older Postgres that rejects the plan_config format.","solutions":["Check the wrapped %v detail in logs for the underlying pq/lib/pq error code and address it specifically (unique_violation, foreign_key_violation, etc.)","Verify the database schema matches migrations (run any pending migrations; confirm plans has id, created_at, updated_at, plan_config columns)","Confirm the org_id, projectId, and userId passed to CreatePlan exist and the DB connection is healthy","If it's a transient connection error, retry CreatePlan; the transaction rolls back cleanly so a retry is safe"],"exampleFix":"// before (hard to diagnose)\nreturn fmt.Errorf(\"error creating plan: %v\", err)\n// after (surface the DB error class)\nvar pqErr *pq.Error\nif errors.As(err, &pqErr) {\n    return fmt.Errorf(\"error creating plan: code=%s constraint=%s: %v\", pqErr.Code, pqErr.Constraint, pqErr)\n}\nreturn fmt.Errorf(\"error creating plan: %w\", err)","handlingStrategy":"try-catch","validationCode":"// Go: verify inputs and DB reachability before CreatePlan\nif orgId == \"\" || projectId == \"\" || userId == \"\" || name == \"\" {\n    return fmt.Errorf(\"create plan: orgId, projectId, userId and name are required\")\n}\nif err := db.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"database unreachable: %w\", err)\n}","typeGuard":"func asPQError(err error) (*pq.Error, bool) {\n    var pqErr *pq.Error\n    if errors.As(err, &pqErr) {\n        return pqErr, true\n    }\n    return nil, false\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) && pqErr.Code == \"23505\" {\n        // duplicate plan: surface a friendly message\n        return nil, ErrPlanAlreadyExists\n    }\n    return nil, fmt.Errorf(\"create plan failed: %w\", err)\n}","preventionTips":["Keep DB migrations in lockstep with the deployed server version","Validate org/project/user ids exist before calling CreatePlan","Handle the specific pq error codes (23505 duplicate, 23503 FK) in the caller","Monitor DB connectivity and pool saturation to catch transient causes"],"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"}