{"record":{"id":"dbf1c4c4ef33ee2c","repo":"gastownhall/beads","slug":"failed-to-iterate-existing-issues-for-prefix-q","errorCode":null,"errorMessage":"failed to iterate existing issues for prefix %q: %w","messagePattern":"failed to iterate existing issues for prefix %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/issues.go","lineNumber":833,"sourceCode":"\t\tvar id string\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to scan issue id: %w\", err)\n\t\t}\n\t\t// Strip the prefix and attempt to parse the remainder as an integer.\n\t\tsuffix := strings.TrimPrefix(id, prefixDash)\n\t\tif suffix == id {\n\t\t\t// id did not start with prefix- (should not happen given LIKE, but be safe)\n\t\t\tcontinue\n\t\t}\n\t\tvar num int\n\t\tif _, parseErr := fmt.Sscanf(suffix, \"%d\", &num); parseErr == nil && fmt.Sprintf(\"%d\", num) == suffix {\n\t\t\tif num > maxNum {\n\t\t\t\tmaxNum = num\n\t\t\t}\n\t\t}\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn fmt.Errorf(\"failed to iterate existing issues for prefix %q: %w\", prefix, err)\n\t}\n\n\t// Only insert a seed row if we found at least one numeric ID.\n\t// If no numeric IDs exist, the counter will naturally start at 1 on first use.\n\tif maxNum > 0 {\n\t\t_, err = tx.ExecContext(ctx,\n\t\t\t\"INSERT INTO issue_counter (prefix, last_id) VALUES (?, ?)\",\n\t\t\tprefix, maxNum)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to seed issue_counter for prefix %q at %d: %w\", prefix, maxNum, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// nextCounterIDTx atomically increments and returns the next sequential issue ID\n// for the given prefix within an existing transaction. Returns the full ID string","sourceCodeStart":815,"sourceCodeEnd":851,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/issues.go#L815-L851","documentation":"After scanning all matching issue ids, rows.Err() is checked; a non-nil error from the result-set iteration (e.g. connection dropped mid-scan) is wrapped with this message. The counter seeding aborts to avoid seeding from partial data.","triggerScenarios":"seedCounterFromExistingIssuesTx iterating the LIKE result set when the connection or query fails partway — network interruption, server timeout, transaction killed.","commonSituations":"Large databases where the scan takes long enough to hit a connection timeout; flaky network to a remote Dolt server; server restart mid-query.","solutions":["Retry the operation once the connection is stable; a full re-scan will reseed correctly","Increase connection/query timeouts for large issue tables","Check server logs and network stability between client and Dolt server","Verify the seeded counter afterwards (next created ID should be max+1)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check connection stability / timeouts before long scans\nif (connTimeout < 30_000) throw new Error(\"increase query timeout for large tables\");","typeGuard":null,"tryCatchPattern":"try {\n  await bd.create(title);\n} catch (e) {\n  if (String(e.message).includes(\"failed to iterate existing issues\")) {\n    await backoff(); // retry; full re-scan reseeds correctly\n  } else throw e;\n}","preventionTips":["Set generous query timeouts for databases with many issues","Use wired/stable network connections for remote Dolt servers","Retry counter seeding — it is deterministic and idempotent","Verify seeded counter (next id = max+1) after recovering from a failure"],"tags":["database","iteration","timeout","counter"],"backgroundTag":"row-iteration-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}