{"record":{"id":"c388c97182494b4f","repo":"gastownhall/beads","slug":"db-update-s-rows-affected-w","errorCode":null,"errorMessage":"db: Update %s: rows affected: %w","messagePattern":"db: Update (.+?): rows affected: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue.go","lineNumber":280,"sourceCode":"\t// status/ownership mutation collides on this shared cell instead of\n\t// silently cell-merging, and the row's RowVersion CAS token advances so\n\t// the \"generic update path changes RowVersion\" contract\n\t// (types.Issue.RowVersion) holds on the proxied backend too.\n\trowLockClause, rowLockArgs := issueops.RowLockClause()\n\tsetClauses = append(setClauses, rowLockClause)\n\targs = append(args, rowLockArgs...)\n\n\targs = append(args, id)\n\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\tq := fmt.Sprintf(\"UPDATE %s SET %s WHERE id = ?\", table, strings.Join(setClauses, \", \"))\n\tres, err := r.runner.ExecContext(ctx, q, args...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"db: Update %s: %w\", id, err)\n\t}\n\trows, err := res.RowsAffected()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"db: Update %s: rows affected: %w\", id, err)\n\t}\n\tif rows == 0 {\n\t\treturn fmt.Errorf(\"db: Update %s: %w\", id, sql.ErrNoRows)\n\t}\n\tif clearLease && !opts.UseWispsTable {\n\t\tif err := issueops.DeleteLeaseInTx(ctx, r.runner, id); err != nil {\n\t\t\treturn fmt.Errorf(\"db: Update %s: clear lease: %w\", id, err)\n\t\t}\n\t}\n\n\t// Event-type parity: embedded records EventClosed / EventReopened /\n\t// EventStatusChanged for status transitions (issueops.DetermineEventType),\n\t// EventUpdated otherwise.\n\teventType := types.EventUpdated\n\tif statusChanging {\n\t\teventType = issueops.DetermineEventType(oldIssue, updates)\n\t}\n\tif err := r.events.Record(ctx, domain.Event{","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue.go#L262-L298","documentation":"Wrapped failure from res.RowsAffected() after the UPDATE executed: the driver could not report how many rows the statement changed. This is a driver/protocol-level problem, distinct from a zero-row result (which yields sql.ErrNoRows instead). The unit of work aborts even though the UPDATE may have been applied.","triggerScenarios":"Calling Update when the underlying driver (dolthub/driver via database/sql) fails to return rows-affected metadata — protocol mismatch, connection in a bad state after ExecContext, or a driver that does not support RowsAffected for this statement.","commonSituations":"Bugs or version skew in the Dolt driver; a connection dropped between ExecContext and RowsAffected; proxying layers that strip result metadata.","solutions":["Check the wrapped driver error and driver/server versions; upgrade the Dolt driver/server to a compatible pair.","Retry the unit of work on a fresh connection; verify whether the update landed (read the row) before re-writing.","Avoid proxy/middleware that interferes with result metadata.","If reproducible, report the driver issue — this path should never error on a healthy server."],"exampleFix":"// before\nif err := repo.Update(ctx, id, updates, actor, opts); err != nil { return err }\n// after: verify state before retrying\nif err := repo.Update(ctx, id, updates, actor, opts); err != nil && isRowsAffectedErr(err) {\n    if iss, _ := repo.Get(ctx, id, opts); iss == nil || !applied(iss, updates) { retry() }\n}","handlingStrategy":"fallback","validationCode":"// detect a driver that cannot report rows affected (best-effort probe)\n_, err := conn.ExecContext(ctx, \"SELECT 1\")\nif err != nil { /* degraded driver — avoid write paths or verify by read-back */ }","typeGuard":null,"tryCatchPattern":"if err := repo.Update(ctx, id, updates, actor, opts); err != nil && strings.Contains(err.Error(), \"rows affected\") {\n    // verify outcome by reading the row before retrying\n    iss, gerr := repo.Get(ctx, id, opts)\n    if gerr == nil && updateApplied(iss, updates) { return nil }\n    return retry(freshCtx)\n}","preventionTips":["Keep the Dolt driver and server versions compatible","Verify the update landed via Get before blind retries","Remove proxies that strip result metadata","Report persistent RowsAffected failures as driver bugs"],"tags":["database","driver","rows-affected"],"backgroundTag":"driver-result-metadata-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}