{"record":{"id":"0d9d5f3771dee321","repo":"gastownhall/beads","slug":"wake-expired-defer-s-rows-affected-w","errorCode":null,"errorMessage":"wake expired defer %s rows affected: %w","messagePattern":"wake expired defer (.+?) rows affected: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/wake_defers.go","lineNumber":126,"sourceCode":"\tvar woken []string\n\tnow := time.Now().UTC()\n\tfor _, id := range expired {\n\t\t// row_lock is rewritten so a concurrent claim/update conflicts at\n\t\t// commit time instead of cell-merging with this write — the same\n\t\t// invariant the lease scheme depends on.\n\t\t//nolint:gosec // G201: table is a hardcoded constant from the caller above.\n\t\tres, err := tx.ExecContext(ctx, fmt.Sprintf(`\n\t\t\tUPDATE %s\n\t\t\tSET status = 'open', defer_until = NULL, updated_at = ?, row_lock = ?\n\t\t\tWHERE id = ? AND status = 'deferred' AND defer_until IS NOT NULL\n\t\t\t  AND defer_until <= UTC_TIMESTAMP()\n\t\t`, table), now, freshRowLock(), id)\n\t\tif err != nil {\n\t\t\treturn woken, fmt.Errorf(\"wake expired defer %s: %w\", id, err)\n\t\t}\n\t\tn, err := res.RowsAffected()\n\t\tif err != nil {\n\t\t\treturn woken, fmt.Errorf(\"wake expired defer %s rows affected: %w\", id, err)\n\t\t}\n\t\tif n == 0 {\n\t\t\tcontinue // rescued concurrently — leave it be\n\t\t}\n\t\tif err := RecordFullEventInTable(ctx, tx, eventsTable, id, types.EventStatusChanged,\n\t\t\tDeferWakeActor, string(types.StatusDeferred), string(types.StatusOpen)); err != nil {\n\t\t\treturn woken, fmt.Errorf(\"record wake event for %s: %w\", id, err)\n\t\t}\n\t\t// A wake is a status change, so it journals as an update. Emitted past\n\t\t// the rows-affected re-check, so a concurrently-rescued bead records\n\t\t// nothing.\n\t\tif err := RecordEventInTx(ctx, tx, EventUpdate, id, DeferWakeActor); err != nil {\n\t\t\treturn woken, err\n\t\t}\n\t\twoken = append(woken, id)\n\t}\n\treturn woken, nil\n}","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/wake_defers.go#L108-L144","documentation":"Thrown when res.RowsAffected() fails after a successful wake UPDATE. Some drivers cannot report rows affected (unsupported statement type, driver limitation, or broken connection), making it impossible to tell whether the row was actually woken. The function conservatively returns the error, though the UPDATE itself succeeded.","triggerScenarios":"Calling WakeExpiredDefersInTx against a driver/backend that does not implement RowsAffected for UPDATE statements, or whose connection died between the UPDATE response and the metadata read.","commonSituations":"Using a minimal/proxy MySQL driver or Dolt version with incomplete RowsAffected support; transaction connection aborted mid-statement.","solutions":["Check the wrapped cause; if it is a driver capability gap, upgrade the driver/Dolt server.","Retry the operation — the guarded UPDATE means concurrent rescues are harmlessly skipped.","If using a proxy or unusual MySQL-compatible backend, verify it forwards the affected-rows count.","As a last resort, log and continue (n unknown) only if your deployment tolerates unreported wake events."],"exampleFix":"// before: incomplete driver\nimport \"github.com/legacy/go-mysql-driver\"\n// after\ndb, err := sql.Open(\"mysql\", dsn) // official go-sql-driver/mysql with full RowsAffected support","handlingStrategy":"fallback","validationCode":"// verify driver supports RowsAffected\nrows, err := db.Exec(\"UPDATE issues SET updated_at=updated_at WHERE id=(SELECT id FROM issues LIMIT 1)\")\nif err != nil { return err }\nif _, err := rows.RowsAffected(); err != nil { log.Print(\"driver lacks RowsAffected support; upgrade driver\") }","typeGuard":null,"tryCatchPattern":"if err != nil {\n    log.Printf(\"RowsAffected unavailable for %s; treating as best-effort wake: %v\", id, err)\n    continue // UPDATE succeeded; only the count is unknown\n}","preventionTips":["Use the official go-sql-driver/mysql or a fully-featured Dolt driver.","Avoid MySQL proxies/backends that strip the affected-rows response.","Upgrade the storage driver when RowsAffected errors appear.","Test RowsAffected support during deployment smoke checks."],"tags":["database","rows-affected","driver-compatibility"],"backgroundTag":"rows-affected-unsupported","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}