{"record":{"id":"f44882ffe468a9de","repo":"plandex-ai/plandex","slug":"error-checking-if-plan-exists","errorCode":null,"errorMessage":"Error checking if plan exists: ","messagePattern":"Error checking if plan exists: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/plans_crud.go","lineNumber":90,"sourceCode":"\tif name == \"draft\" {\n\t\t// delete any existing draft plans\n\t\terr = db.DeleteDraftPlans(auth.OrgId, projectId, auth.User.Id)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error deleting draft plans: %v\\n\", err)\n\t\t\thttp.Error(w, \"Error deleting draft plans: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\ti := 2\n\t\toriginalName := name\n\t\tfor {\n\t\t\tvar count int\n\t\t\terr := db.Conn.Get(&count, \"SELECT COUNT(*) FROM plans WHERE project_id = $1 AND owner_id = $2 AND name = $3\", projectId, auth.User.Id, name)\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Error checking if plan exists: %v\\n\", err)\n\t\t\t\thttp.Error(w, \"Error checking if plan exists: \"+err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif count == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tname = originalName + \".\" + fmt.Sprint(i)\n\t\t\ti++\n\t\t}\n\t}\n\n\tplan, err := db.CreatePlan(r.Context(), auth.OrgId, projectId, auth.User.Id, name)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error creating plan: %v\\n\", err)\n\t\thttp.Error(w, \"Error creating plan: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_crud.go#L72-L108","documentation":"CreatePlanHandler returns this 500 when the sqlx query that counts existing plans with the same project_id/owner_id/name fails. It is part of the duplicate-name auto-suffix loop, so the handler cannot proceed to create the plan. It surfaces the underlying database error text in the response body.","triggerScenarios":"POST to create a plan with a non-empty name (not 'draft') while db.Conn.Get on the plans table errors: plans table missing/migrated differently, database unreachable, connection pool exhausted, or a schema/type mismatch binding the count into int.","commonSituations":"Postgres not running or wrong DATABASE_URL after a deploy; partial migration leaving plans table columns missing (e.g. owner_id); DB connection dropped mid-request (TLS timeout, restart); replica failover.","solutions":["Check server logs for the wrapped database error text appended after the prefix to identify the root cause","Verify the Postgres instance is reachable and DATABASE_URL is correct (try psql with the same DSN)","Run migrations to ensure the plans table exists with project_id, owner_id and name columns","Check connection pool settings/limits (max conns, idle timeout) and restart saturated database connections"],"exampleFix":"// before\nerr := db.Conn.Get(&count, \"SELECT COUNT(*) FROM plans WHERE project_id = $1 AND owner_id = $2 AND name = $3\", projectId, auth.User.Id, name)\n// after\nif db.Conn == nil { http.Error(w, \"database not initialized\", http.StatusInternalServerError); return }\nerr := db.Conn.Get(&count, \"SELECT COUNT(*) FROM plans WHERE project_id = $1 AND owner_id = $2 AND name = $3\", projectId, auth.User.Id, name)","handlingStrategy":"retry","validationCode":"if err := db.Conn.Ping(); err != nil { return fmt.Errorf(\"database unreachable: %w\", err) }","typeGuard":"func isDBError(err error) bool { var pgErr *pgconn.PgError; return errors.As(err, &pgErr) }","tryCatchPattern":"if err := db.Conn.Get(&count, q, args...); err != nil {\n\tif isTransientDBErr(err) { /* backoff and retry once */ }\n\tlog.Printf(\"Error checking if plan exists: %v\\n\", err)\n\thttp.Error(w, \"Error checking if plan exists\", http.StatusInternalServerError)\n\treturn\n}","preventionTips":["Ping or health-check the database before serving requests and on startup","Keep migrations applied so plans table/columns exist in every environment","Set sane connection pool limits and idle timeouts to avoid stale connections","Monitor Postgres availability and alert on connection errors"],"tags":["database","sql","postgres","server-error"],"backgroundTag":"database-query-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"}