{"record":{"id":"4fa21f7a4233bb38","repo":"gastownhall/beads","slug":"failed-to-check-rows-affected-after-seeding-for-pr","errorCode":null,"errorMessage":"failed to check rows affected after seeding for prefix %q: %w","messagePattern":"failed to check rows affected after seeding for prefix %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/issues.go","lineNumber":882,"sourceCode":"\trowsAffected, err := res.RowsAffected()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to check rows affected for issue counter prefix %q: %w\", prefix, err)\n\t}\n\n\tif rowsAffected == 0 {\n\t\t// No counter row yet - seed from existing issues before proceeding to\n\t\t// avoid collisions with manually-created sequential IDs (GH#2002).\n\t\tif seedErr := seedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to seed issue counter for prefix %q: %w\", prefix, seedErr)\n\t\t}\n\t\t// Retry the atomic increment after seeding.\n\t\tres, err = tx.ExecContext(ctx, \"UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?\", prefix)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to increment issue counter after seeding for prefix %q: %w\", prefix, err)\n\t\t}\n\t\trowsAffected, err = res.RowsAffected()\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to check rows affected after seeding for prefix %q: %w\", prefix, err)\n\t\t}\n\t\tif rowsAffected == 0 {\n\t\t\t// Seeding found no existing numeric IDs -- insert the initial row.\n\t\t\t_, err = tx.ExecContext(ctx, \"INSERT INTO issue_counter (prefix, last_id) VALUES (?, 1)\", prefix)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"failed to insert initial issue counter for prefix %q: %w\", prefix, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Read back the value that was atomically set by the DB engine.\n\tvar nextID int\n\terr = tx.QueryRowContext(ctx, \"SELECT last_id FROM issue_counter WHERE prefix = ?\", prefix).Scan(&nextID)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read issue counter after increment for prefix %q: %w\", prefix, err)\n\t}\n\treturn fmt.Sprintf(\"%s-%d\", prefix, nextID), nil\n}","sourceCodeStart":864,"sourceCodeEnd":900,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/issues.go#L864-L900","documentation":"This wraps a failure of res.RowsAffected() on the post-seeding retry UPDATE, mirroring error 2571 but on the second execution path. The driver failed to report how many rows the retried increment touched, so nextCounterIDTx cannot decide whether to insert an initial row. The code needs this value to distinguish 'counter row exists' from 'no numeric IDs found during seeding'.","triggerScenarios":"Counter-mode ID generation where the first UPDATE missed (no row), seeding ran, the retry UPDATE executed, but the driver's RowsAffected() call errored — driver protocol issue, connection degraded between exec and metadata read, or unsupported statement metadata.","commonSituations":"Mismatched Dolt driver/server versions; remote Dolt over an unstable connection; custom storage driver that doesn't implement RowsAffected correctly.","solutions":["Read the wrapped cause and upgrade/repair the Dolt driver to a version with reliable RowsAffected support.","Test connectivity to the Dolt server; restart it if the connection state is suspect.","Replace the driver if a custom/proxy driver strips affected-row metadata.","Upgrade beads so the counter path tolerates missing rows-affected info via a read-back fallback."],"exampleFix":"// before\nrowsAffected, err = res.RowsAffected()\nif err != nil {\n    return \"\", fmt.Errorf(\"failed to check rows affected after seeding for prefix %q: %w\", prefix, err)\n}\n// after\nrowsAffected, err = res.RowsAffected()\nif err != nil {\n    var cur int\n    switch scanErr := tx.QueryRowContext(ctx, \"SELECT last_id FROM issue_counter WHERE prefix = ?\", prefix).Scan(&cur); {\n    case scanErr == nil:\n        rowsAffected = 1\n    case scanErr == sql.ErrNoRows:\n        rowsAffected = 0\n    default:\n        return \"\", fmt.Errorf(\"failed to check rows affected after seeding for prefix %q: %w\", prefix, err)\n    }\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"func isRowsAffectedErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"rows affected\")\n}","tryCatchPattern":"id, err := store.CreateIssue(ctx, issue)\nif err != nil && strings.Contains(err.Error(), \"check rows affected after seeding\") {\n    log.Printf(\"driver RowsAffected unsupported on post-seed path: %v\", err)\n    // upgrade driver/beads; transient failures may succeed on retry\n}","preventionTips":["Use the officially supported Dolt driver version","Avoid custom sql.driver wrappers that don't implement RowsAffected","Verify affected-row behavior after every driver/server upgrade","Keep connections healthy: avoid sharing the DB across process boundaries"],"tags":["database","driver","rows-affected","dolt"],"backgroundTag":"rows-affected-unavailable","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}