{"record":{"id":"3a090feceb1afad9","repo":"gastownhall/beads","slug":"is-blocked-rows-from-s-w","errorCode":null,"errorMessage":"is_blocked rows from %s: %w","messagePattern":"is_blocked rows from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_queries.go","lineNumber":1053,"sourceCode":"\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar id string\n\t\t\tvar b int\n\t\t\tif err := rows.Scan(&id, &b); err != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn fmt.Errorf(\"scan is_blocked from %s: %w\", table, err)\n\t\t\t}\n\t\t\t// Keep the first-seen (issues) value and skip any later (wisps)\n\t\t\t// duplicate, so the batch is_blocked matches per-row IsBlocked.\n\t\t\tif seen[id] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tseen[id] = true\n\t\t\tblocked[id] = b != 0\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn fmt.Errorf(\"is_blocked rows from %s: %w\", table, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// scanDependencyRow scans a single dependency row from a *sql.Rows.\nfunc scanDependencyRow(rows *sql.Rows) (*types.Dependency, error) {\n\tvar dep types.Dependency\n\tvar createdAt sql.NullTime\n\tvar metadata, threadID sql.NullString\n\n\tif err := rows.Scan(&dep.IssueID, &dep.DependsOnID, &dep.Type, &createdAt, &dep.CreatedBy, &metadata, &threadID); err != nil {\n\t\treturn nil, fmt.Errorf(\"scan dependency: %w\", err)\n\t}\n\n\tif createdAt.Valid {\n\t\tdep.CreatedAt = createdAt.Time\n\t}","sourceCodeStart":1035,"sourceCodeEnd":1071,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_queries.go#L1035-L1071","documentation":"Wraps rows.Err() after iterating is_blocked rows in readIsBlockedIntoFromTable. Scan can succeed per-row yet the iterator can still fail; this check catches transport/query errors surfaced at the end of iteration (driver disconnect, query killed, server error mid-stream). The library throws it so partial batch results are never returned as if complete.","triggerScenarios":"IsBlockedBatchInTx runs, rows iterate normally, but the Dolt connection drops or the query is cancelled/timed out before the result set is fully drained, so rows.Err() is non-nil.","commonSituations":"Long batch is_blocked queries over flaky connections; context timeout cancelling the query mid-iteration; Dolt server restart during a large batch check.","solutions":["Retry the operation; rows.Err() here is usually transient (connection or context issue).","Check context deadlines: increase timeout or pass a context with adequate budget for large batches.","Verify Dolt server/network stability and connection pool settings.","Reduce batch size so each query completes well within timeouts."],"exampleFix":"// before: no deadline, fails mid-iteration\nblocked, err := ops.IsBlockedBatchInTx(ctx, tx, ids)\n// after: bounded, retryable context\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\nblocked, err := ops.IsBlockedBatchInTx(ctx, tx, ids)\nif err != nil { return retry(ctx, ...) }","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil {\n\treturn fmt.Errorf(\"context already done before batch is_blocked: %w\", err)\n}\nif err := txPlaceholder.PingContext(ctx); err != nil {\n\treturn fmt.Errorf(\"connection dead before batch: %w\", err)\n}","typeGuard":"func isTransientRowsErr(err error) bool {\n\treturn errors.Is(err, context.DeadlineExceeded) ||\n\t\terrors.Is(err, context.Canceled) ||\n\t\terrors.Is(err, driver.ErrBadConn)\n}","tryCatchPattern":"err := ops.IsBlockedBatchInTx(ctx, tx, ids)\nfor attempt := 0; isTransientRowsErr(err) && attempt < 3; attempt++ {\n\ttime.Sleep(backoff(attempt))\n\terr = ops.IsBlockedBatchInTx(ctx, tx, ids)\n}\nif err != nil { return err }","preventionTips":["Give batch operations generous, explicit context deadlines.","Keep batch sizes small so result sets drain quickly.","Monitor Dolt server health and connection pool saturation.","Never ignore rows.Err() in your own row loops; treat it as retryable."],"tags":["sql","rows-err","storage","transient"],"backgroundTag":"sql-rows-iteration-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}