{"record":{"id":"2135b2632efdb970","repo":"plandex-ai/plandex","slug":"error-setting-plan-status-v","errorCode":null,"errorMessage":"error setting plan status: %v","messagePattern":"error setting plan status: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/plan_helpers.go","lineNumber":268,"sourceCode":"}\n\nfunc GetPlan(planId string) (*Plan, error) {\n\tvar plan Plan\n\n\terr := Conn.Get(&plan, \"SELECT * FROM plans WHERE id = $1\", planId)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting plan: %v\", err)\n\t}\n\n\treturn &plan, nil\n}\n\nfunc SetPlanStatus(planId, branch string, status shared.PlanStatus, errStr string) error {\n\t_, err := Conn.Exec(\"UPDATE branches SET status = $1, error = $2 WHERE plan_id = $3 AND name = $4\", status, errStr, planId, branch)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error setting plan status: %v\", err)\n\t}\n\n\treturn nil\n}\n\nfunc RenamePlan(planId string, name string, tx *sqlx.Tx) error {\n\tvar err error\n\tif tx == nil {\n\t\t_, err = Conn.Exec(\"UPDATE plans SET name = $1 WHERE id = $2\", name, planId)\n\t} else {\n\t\t_, err = tx.Exec(\"UPDATE plans SET name = $1 WHERE id = $2\", name, planId)\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error renaming plan: %v\", err)\n\t}\n\n\treturn nil","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/plan_helpers.go#L250-L286","documentation":"SetPlanStatus writes status and error text to the branches row via UPDATE branches SET status = $1, error = $2 WHERE plan_id = $3 AND name = $4. This wrapper fires when that UPDATE fails at the DB level. It is called from many places (streams, builds, tell/exec handlers), so a failure here typically means plan lifecycle state cannot be persisted while other plan operations may be in flight.","triggerScenarios":"The branches UPDATE errors: DB connection dropped mid-stream, branches row missing (no rows affected — silent, but constraint errors possible), status enum value not matching the column type, or table lock timeout under concurrent stream updates.","commonSituations":"Many concurrent model streams updating status on the same branch causing lock contention; passing a PlanStatus value not accepted by the DB column type; Postgres restart during a long-running Build; wrong branch name combined with a schema constraint.","solutions":["Check the inner error for connection/lock issues and verify DB health and pool sizing","Confirm the PlanStatus value being set is valid for the branches.status column type/enum","Ensure the planId+branch pair identifies an existing branches row; missing rows don't error but stale/wrong pairs may cause downstream constraint failures","Add retry with backoff around SetPlanStatus for transient connection errors, especially in stream-finish paths"],"exampleFix":"// before\n_, err := Conn.Exec(\"UPDATE branches SET status = $1, error = $2 WHERE plan_id = $3 AND name = $4\", status, errStr, planId, branch)\n// after\nres, err := Conn.Exec(\"UPDATE branches SET status = $1, error = $2 WHERE plan_id = $3 AND name = $4\", status, errStr, planId, branch)\nif err == nil {\n    if n, _ := res.RowsAffected(); n == 0 {\n        err = fmt.Errorf(\"no branches row for plan %s branch %s\", planId, branch)\n    }\n}","handlingStrategy":"retry","validationCode":"var exists bool\nif err := Conn.Get(&exists, \"SELECT EXISTS(SELECT 1 FROM branches WHERE plan_id = $1 AND name = $2)\", planId, branch); err != nil || !exists {\n    return fmt.Errorf(\"branch %s not found for plan %s\", branch, planId)\n}","typeGuard":"func branchRowExists(planId, branch string) bool {\n    var exists bool\n    _ = Conn.Get(&exists, \"SELECT EXISTS(SELECT 1 FROM branches WHERE plan_id = $1 AND name = $2)\", planId, branch)\n    return exists\n}","tryCatchPattern":"err := SetPlanStatus(planId, branch, shared.PlanStatusRunning, \"\")\nif err != nil {\n    if isTransientDBError(err) {\n        time.Sleep(time.Second)\n        err = SetPlanStatus(planId, branch, shared.PlanStatusRunning, \"\")\n    }\n    if err != nil {\n        log.Printf(\"status update failed for plan %s/%s: %v\", planId, branch, err)\n    }\n}","preventionTips":["Retry transient connection errors with backoff, especially in stream-finish handlers","Verify status values match the branches.status column type","Check RowsAffected to detect silently-missing branches rows","Minimize concurrent status writes per branch to reduce lock contention"],"tags":["database","postgresql","go","state-management"],"backgroundTag":"database-update-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}