{"record":{"id":"6e974e0e7af741e0","repo":"plandex-ai/plandex","slug":"error-listing-plans-v","errorCode":null,"errorMessage":"error listing plans: %v","messagePattern":"error listing plans: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/plan_helpers.go","lineNumber":113,"sourceCode":"}\n\nfunc ListOwnedPlans(projectIds []string, userId string, archived bool) ([]*Plan, error) {\n\tqs := \"SELECT * FROM plans WHERE project_id = ANY($1) AND owner_id = $2\"\n\tqargs := []interface{}{pq.Array(projectIds), userId}\n\n\tif archived {\n\t\tqs += \" AND archived_at IS NOT NULL\"\n\t} else {\n\t\tqs += \" AND archived_at IS NULL\"\n\t}\n\n\tqs += \" ORDER BY updated_at DESC\"\n\n\tvar plans []*Plan\n\terr := Conn.Select(&plans, qs, qargs...)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error listing plans: %v\", err)\n\t}\n\n\treturn plans, nil\n}\n\nfunc GetPlanNamesById(planIds []string) (map[string]string, error) {\n\tvar plans []*Plan\n\terr := Conn.Select(&plans, \"SELECT id, name FROM plans WHERE id = ANY($1)\", pq.Array(planIds))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting plan names: %v\", err)\n\t}\n\n\tnamesMap := make(map[string]string)\n\tfor _, plan := range plans {\n\t\tnamesMap[plan.Id] = plan.Name\n\t}\n\n\treturn namesMap, nil","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/plan_helpers.go#L95-L131","documentation":"This error wraps a failure of Conn.Select(&plans, qs, qargs...) in ListOwnedPlans (app/server/db/plan_helpers.go:113). The function queries all plans for a set of project ids owned by a user, filtered by archived status, using sqlx Select to scan rows directly into []*Plan. It surfaces SQL execution errors and row-scan/destination mapping errors alike.","triggerScenarios":"Called by ListPlansHandler, ListArchivedPlansHandler, ListPlansRunningHandler, and GetCurrentBranchByPlanIdHandler; fails when the plans table is missing or altered (SELECT * columns no longer match the Plan struct), when the database connection is down, when projectIds is invalid for pq.Array, or when a scan target type mismatches a column type after a migration.","commonSituations":"Deployed code newer than the DB schema (or vice versa) so SELECT * columns don't match Plan fields, transient Postgres connection failures, empty projectIds slice producing unexpected array binding, or connection-pool exhaustion under load.","solutions":["Read the wrapped %v detail; if it's a scan/columns mismatch, run pending migrations or redeploy the matching server version","Check database connectivity and pool health; retry on transient connection errors","Ensure the caller passes a non-nil projectIds slice; if empty, short-circuit to return an empty list instead of querying","If SELECT * keeps breaking on schema changes, enumerate explicit columns in the query so scans stay stable"],"exampleFix":"// before\nqs := \"SELECT * FROM plans WHERE project_id = ANY($1) AND owner_id = $2\"\n// after (explicit columns survive schema drift; guard empty input)\nif len(projectIds) == 0 {\n    return []*Plan{}, nil\n}\nqs := \"SELECT id, org_id, owner_id, project_id, name, plan_config, archived_at, shared_with_org_at, created_at, updated_at FROM plans WHERE project_id = ANY($1) AND owner_id = $2\"","handlingStrategy":"retry","validationCode":"// Go: pre-flight before ListOwnedPlans\nif projectIds == nil {\n    projectIds = []string{}\n}\nif err := Conn.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"database unreachable: %w\", err)\n}","typeGuard":"func isRetryableDBError(err error) bool {\n    var pqErr *pq.Error\n    if errors.As(err, &pqErr) {\n        switch pqErr.Code {\n        case \"08006\", \"08001\", \"57P01\", \"53300\": // connection failures, admin shutdown, too many connections\n            return true\n        }\n        return false\n    }\n    return errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"plans, err := db.ListOwnedPlans(projectIds, userId, archived)\nif err != nil {\n    if isRetryableDBError(err) {\n        plans, err = retryWithBackoff(3, time.Second, func() ([]*db.Plan, error) {\n            return db.ListOwnedPlans(projectIds, userId, archived)\n        })\n    }\n    if err != nil {\n        http.Error(w, \"could not load plans\", http.StatusInternalServerError)\n        return\n    }\n}","preventionTips":["Keep the Plan struct and DB schema in sync; run migrations on every deploy","Prefer explicit column lists over SELECT * to survive schema drift","Set sane connection-pool limits and monitor pool exhaustion (53300)","Guard empty projectIds input at the handler layer before querying"],"tags":["database","postgres","sql","query"],"backgroundTag":"sql-query-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"}