gastownhall/beads · error
db: ChildCounterSQLRepository.NextChildID: rows: %w
Error message
db: ChildCounterSQLRepository.NextChildID: rows: %w
What it means
After iterating child IDs, NextChildID calls rows.Err() to surface any error that occurred during iteration (driver I/O, query canceled, connection lost). This error wraps that post-iteration check. It means the result stream from the child-id query failed partway, so the computed `lastChild` is unreliable and no child ID was produced.
Source
Thrown at internal/storage/domain/db/child_counter.go:72
SELECT id FROM %s
WHERE id LIKE CONCAT(?, '.%%')
AND id NOT LIKE CONCAT(?, '.%%.%%')
`, issueTable), parentID, parentID) //nolint:gosec // G201: issueTable is one of two hardcoded constants
if err != nil {
return "", fmt.Errorf("db: ChildCounterSQLRepository.NextChildID: scan existing children of %s: %w", parentID, err)
}
defer rows.Close()
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
return "", fmt.Errorf("db: ChildCounterSQLRepository.NextChildID: scan: %w", err)
}
if n, ok := parseChildSuffix(id); ok && n > lastChild {
lastChild = n
}
}
if err := rows.Err(); err != nil {
return "", fmt.Errorf("db: ChildCounterSQLRepository.NextChildID: rows: %w", err)
}
next := lastChild + 1
//nolint:gosec // G201: counterTable is one of two hardcoded constants
if _, err := r.runner.ExecContext(ctx, fmt.Sprintf(`
INSERT INTO %s (parent_id, last_child) VALUES (?, ?)
ON DUPLICATE KEY UPDATE last_child = ?
`, counterTable), parentID, next, next); err != nil {
return "", fmt.Errorf("db: ChildCounterSQLRepository.NextChildID: upsert counter for %s: %w", parentID, err)
}
return fmt.Sprintf("%s.%d", parentID, next), nil
}
func (r *childCounterSQLRepositoryImpl) parentIsActiveWisp(ctx context.Context, parentID string) (bool, error) {
var probe int
err := r.runner.QueryRowContext(ctx, "SELECT 1 FROM wisps WHERE id = ? LIMIT 1", parentID).Scan(&probe)
switch {View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped driver error (errors.Unwrap) — context.DeadlineExceeded means increase timeout or optimize the child query.
- Retry the operation; the upsert only happens after this check so a retry is safe.
- Verify network stability between the app and the Dolt/MySQL server (timeouts, proxies).
- If cancelations are intentional, use a longer context for NextChildID during bulk operations.
Example fix
// before: bare context cancelled mid-iteration ctx := context.Background() // after: give child-scan queries an explicit generous deadline ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel()
Defensive patterns
Strategy: retry
Validate before calling
if err := ctx.Err(); err != nil {
return fmt.Errorf("context already done before NextChildID: %w", err)
} Type guard
func isIterationError(err error) bool {
return err != nil && strings.Contains(err.Error(), "NextChildID: rows:")
} Try / catch
for attempt := 0; attempt < 3; attempt++ {
id, err := repo.NextChildID(ctx, parentID, opts)
if err == nil { return id, nil }
if !isIterationError(err) || ctx.Err() != nil { return "", err }
time.Sleep(backoff(attempt))
} Prevention
- Use generous timeouts for child-creation flows
- Keep issues table size in check; archive old wisps
- Monitor network health to the Dolt server
When it happens
Trigger: Calling NextChildID while the underlying connection breaks during rows iteration, the context is canceled (Ctrl-C, deadline exceeded), or the server kills the query mid-stream.
Common situations: Long-running scans over large issue tables hitting network timeouts; context deadline too short for big parents; server-side wait_timeout dropping idle connections.
Related errors
- db: CommentSQLRepository.CountsByIssueIDs: rows: %w
- db: CommentSQLRepository.ListByIssueIDs: rows: %w
- get dependencies: rows from %s: %w
- get dependents: rows from %s: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/142bd25712a5e50b.
Report an issue: GitHub.