{"record":{"id":"f99c52ce65d8198e","repo":"gastownhall/beads","slug":"get-dependents-rows-from-s-w","errorCode":null,"errorMessage":"get dependents: rows from %s: %w","messagePattern":"get dependents: rows from (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":1185,"sourceCode":"\t// Query both dependency tables to find all dependents.\n\tvar deps []depMeta\n\tfor _, depTable := range []string{\"dependencies\", \"wisp_dependencies\"} {\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(\n\t\t\t`SELECT issue_id, type FROM %s WHERE %s = ?`, depTable, DepTargetExpr), issueID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get dependents from %s: %w\", depTable, err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar d depMeta\n\t\t\tif scanErr := rows.Scan(&d.depID, &d.depType); scanErr != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn nil, fmt.Errorf(\"get dependents: scan: %w\", scanErr)\n\t\t\t}\n\t\t\tdeps = append(deps, d)\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get dependents: rows from %s: %w\", depTable, err)\n\t\t}\n\t}\n\n\tif len(deps) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Fetch all dependent issues.\n\tids := make([]string, len(deps))\n\tfor i, d := range deps {\n\t\tids[i] = d.depID\n\t}\n\tissues, err := GetIssuesByIDsInTx(ctx, tx, ids, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get dependents: fetch issues: %w\", err)\n\t}\n\tissueMap := make(map[string]*types.Issue, len(issues))\n\tfor _, iss := range issues {","sourceCodeStart":1167,"sourceCodeEnd":1203,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L1167-L1203","documentation":"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.","triggerScenarios":"Calling GetDependentsWithMetadataInTx when the connection drops or ctx is cancelled while draining dependent rows from dependencies/wisp_dependencies.","commonSituations":"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.","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."],"exampleFix":"// before: pool outlives server wait_timeout\nsqlDB.SetConnMaxLifetime(0)\n// after\nsqlDB.SetConnMaxLifetime(4 * time.Minute)\nsqlDB.SetConnMaxIdleTime(1 * time.Minute)","handlingStrategy":"retry","validationCode":"// Confirm connectivity and budget before the call\nif err := tx.PingContext(ctx); err != nil { return err }\n_, ok := ctx.Deadline()\nif !ok { ctx, cancel := context.WithTimeout(ctx, 30*time.Second); defer cancel(); _ = ok }","typeGuard":"func isTransientRowsErr(err error) bool {\n    return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) || strings.Contains(err.Error(), \"bad connection\")\n}","tryCatchPattern":"var deps []*types.IssueWithDependencyMetadata\nerr := retry.Do(3, func() error {\n    var e error\n    deps, e = GetDependentsWithMetadataInTx(ctx, tx, issueID)\n    return e\n})\nif err != nil { return err }","preventionTips":["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."],"tags":["database","sql","go","rows-iteration"],"backgroundTag":"sql-rows-iteration-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}