gastownhall/beads · error
get dependents: rows from %s: %w
Error message
get dependents: rows from %s: %w
What it means
This wraps rows.Err() failing after the dependent-row iteration completes in GetDependentsWithMetadataInTx. database/sql defers some driver errors (connection loss, context cancellation, protocol errors) to the end of iteration; this is where they surface. The message names the table whose row stream failed.
Source
Thrown at internal/storage/issueops/dependencies.go:1185
// Query both dependency tables to find all dependents.
var deps []depMeta
for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
rows, err := tx.QueryContext(ctx, fmt.Sprintf(
`SELECT issue_id, type FROM %s WHERE %s = ?`, depTable, DepTargetExpr), issueID)
if err != nil {
return nil, fmt.Errorf("get dependents from %s: %w", depTable, err)
}
for rows.Next() {
var d depMeta
if scanErr := rows.Scan(&d.depID, &d.depType); scanErr != nil {
_ = rows.Close()
return nil, fmt.Errorf("get dependents: scan: %w", scanErr)
}
deps = append(deps, d)
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("get dependents: rows from %s: %w", depTable, err)
}
}
if len(deps) == 0 {
return nil, nil
}
// Fetch all dependent issues.
ids := make([]string, len(deps))
for i, d := range deps {
ids[i] = d.depID
}
issues, err := GetIssuesByIDsInTx(ctx, tx, ids, nil)
if err != nil {
return nil, fmt.Errorf("get dependents: fetch issues: %w", err)
}
issueMap := make(map[string]*types.Issue, len(issues))
for _, iss := range issues {View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error to see the driver cause (context deadline, bad connection, etc.).
- Tune connection settings: ConnMaxLifetime below server wait_timeout, keepalives enabled.
- Ensure the caller's context has enough budget for the whole dependent traversal.
- Retry the read on a fresh transaction/connection if transient.
Example fix
// before: pool outlives server wait_timeout sqlDB.SetConnMaxLifetime(0) // after sqlDB.SetConnMaxLifetime(4 * time.Minute) sqlDB.SetConnMaxIdleTime(1 * time.Minute)
Defensive patterns
Strategy: retry
Validate before calling
// Confirm connectivity and budget before the call
if err := tx.PingContext(ctx); err != nil { return err }
_, ok := ctx.Deadline()
if !ok { ctx, cancel := context.WithTimeout(ctx, 30*time.Second); defer cancel(); _ = ok } Type guard
func isTransientRowsErr(err error) bool {
return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "bad connection")
} Try / catch
var deps []*types.IssueWithDependencyMetadata
err := retry.Do(3, func() error {
var e error
deps, e = GetDependentsWithMetadataInTx(ctx, tx, issueID)
return e
})
if err != nil { return err } Prevention
- Retry idempotent reads on transient failures with backoff.
- Set ConnMaxLifetime/ConnMaxIdleTime below server timeouts.
- Enable TCP keepalives on the DB connection string.
- Don't cancel parent contexts mid-traversal; budget for full fan-out.
When it happens
Trigger: Calling GetDependentsWithMetadataInTx when the connection drops or ctx is cancelled while draining dependent rows from dependencies/wisp_dependencies.
Common situations: Idle connection reaped by the server mid-query; request deadline exceeded during a large dependent fan-out; network interruption on remote DB; proxy/load-balancer killing long-lived connections.
Related errors
- db: ChildCounterSQLRepository.NextChildID: rows: %w
- db: CommentSQLRepository.CountsByIssueIDs: rows: %w
- db: CommentSQLRepository.ListByIssueIDs: rows: %w
- get dependencies: rows from %s: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f99c52ce65d8198e.
Report an issue: GitHub.