{"record":{"id":"3ff0f9999fafce72","repo":"plandex-ai/plandex","slug":"error-getting-rows-affected-3ff0f9","errorCode":null,"errorMessage":"Error getting rows affected: ","messagePattern":"Error getting rows affected: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/plans_crud.go","lineNumber":244,"sourceCode":"\n\tif plan.OwnerId != auth.User.Id {\n\t\tlog.Println(\"Only the plan owner can delete a plan\")\n\t\thttp.Error(w, \"Only the plan owner can delete a plan\", http.StatusForbidden)\n\t\treturn\n\t}\n\n\tres, err := db.Conn.Exec(\"DELETE FROM plans WHERE id = $1\", planId)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error deleting plan: %v\\n\", err)\n\t\thttp.Error(w, \"Error deleting plan: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\trowsAffected, err := res.RowsAffected()\n\tif err != nil {\n\t\tlog.Printf(\"Error getting rows affected: %v\\n\", err)\n\t\thttp.Error(w, \"Error getting rows affected: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif rowsAffected == 0 {\n\t\tlog.Println(\"Plan not found\")\n\t\thttp.Error(w, \"Not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\terr = db.DeletePlanDir(auth.OrgId, planId)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error deleting plan dir: %v\\n\", err)\n\t\thttp.Error(w, \"Error deleting plan dir: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tlog.Println(\"Successfully deleted plan\", planId)","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_crud.go#L226-L262","documentation":"In DeletePlanHandler (app/server/handlers/plans_crud.go:244), the DELETE statement against the plans table succeeded, but calling res.RowsAffected() on the driver's result returned an error. This is a database/sql driver-level failure: the driver could not report how many rows the DELETE removed. It is almost always a driver/DB connectivity or capability issue, not an application-logic bug.","triggerScenarios":"DELETE FROM plans WHERE id = $1 executes without error via db.Conn.Exec, but res.RowsAffected() fails — e.g. the Postgres connection dropped mid-response, the driver (lib/pq or pgx stdlib) cannot retrieve the command tag, or a non-supporting driver wrapper returns an error from RowsAffected.","commonSituations":"Connection pool exhaustion or a DB restart between Exec and RowsAffected; using a driver or transaction wrapper that does not implement RowsAffected; network interruption in long-lived server processes.","solutions":["Check server logs for the raw driver error printed alongside this message and fix the underlying DB connectivity issue (pool limits, restarts, network).","Verify the Postgres driver in use (lib/pq / pgx stdlib) is up to date and properly implements RowsAffected.","Use a health-checked connection pool (ping, idle timeouts) so dead connections are recycled before Exec.","If the DB row was deleted but the response was 500, treat the delete as idempotent: retrying will return 404 'Not found', not duplicate deletion."],"exampleFix":"// before\nres, err := db.Conn.Exec(\"DELETE FROM plans WHERE id = $1\", planId)\n// after\nctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)\ndefer cancel()\nres, err := db.Conn.ExecContext(ctx, \"DELETE FROM plans WHERE id = $1\", planId)","handlingStrategy":"try-catch","validationCode":"if err := db.Conn.PingContext(ctx); err != nil {\n    // DB connection unhealthy — do not attempt the delete\n}","typeGuard":"func isDriverResultErr(err error) bool {\n    var pgErr *pgconn.PgError\n    return errors.As(err, &pgErr)\n}","tryCatchPattern":"res, err := db.Conn.ExecContext(ctx, \"DELETE FROM plans WHERE id = $1\", planId)\nif err != nil { return }\nrowsAffected, err := res.RowsAffected()\nif err != nil {\n    log.Printf(\"rows affected unavailable, treating as success: %v\", err)\n    return // row delete already committed; do not fail the request\n}","preventionTips":["Ping the DB or use health-checked pooled connections before writes.","Set ExecContext timeouts so dead connections fail fast.","Keep the Postgres driver updated.","Treat the delete as committed once Exec succeeds; never re-delete on RowsAffected failure without checking existence."],"tags":["database","go","http-500","rows-affected"],"backgroundTag":"database-rows-affected-error","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"}