{"record":{"id":"9b02a9730feda3e6","repo":"gastownhall/beads","slug":"partition-wisp-ids-rows-w","errorCode":null,"errorMessage":"partition wisp ids: rows: %w","messagePattern":"partition wisp ids: rows: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/wisp_routing.go","lineNumber":176,"sourceCode":"\t\t\targs...)\n\t\tif qErr != nil {\n\t\t\t// Wisps table may not exist yet on older schemas — treat as \"no wisps\".\n\t\t\tif isTableNotExistError(qErr) {\n\t\t\t\treturn nil, append([]string(nil), ids...), nil\n\t\t\t}\n\t\t\treturn nil, nil, fmt.Errorf(\"partition wisp ids: %w\", qErr)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar id string\n\t\t\tif scanErr := rows.Scan(&id); scanErr != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn nil, nil, fmt.Errorf(\"partition wisp ids: scan: %w\", scanErr)\n\t\t\t}\n\t\t\twispSet[id] = struct{}{}\n\t\t}\n\t\t_ = rows.Close()\n\t\tif rowsErr := rows.Err(); rowsErr != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"partition wisp ids: rows: %w\", rowsErr)\n\t\t}\n\t}\n\n\tfor _, id := range ids {\n\t\tif _, ok := wispSet[id]; ok {\n\t\t\twispIDs = append(wispIDs, id)\n\t\t} else {\n\t\t\tpermIDs = append(permIDs, id)\n\t\t}\n\t}\n\treturn wispIDs, permIDs, nil\n}\n\n// WispTableRouting returns the appropriate issue, label, event, and dependency\n// table names based on whether the ID is an active wisp. Call IsActiveWispInTx\n// first to determine isWisp.\nfunc WispTableRouting(isWisp bool) (issueTable, labelTable, eventTable, depTable string) {\n\tif isWisp {","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/wisp_routing.go#L158-L194","documentation":"After draining the result rows of the wisps IN-query, PartitionWispIDsInTx checks rows.Err(). This error wraps any error accumulated during iteration that was not surfaced by Scan — most commonly a lost connection or context cancellation while streaming rows. The built wispSet is discarded; no partial partition is returned.","triggerScenarios":"Calling PartitionWispIDsInTx (directly or via GetCommentsForIssuesInTx, GetCommentCountsInTx, ResolveDeletionSetInTx, GetDependencyRecordsForIssuesInTx, GetBlockingInfoForIssuesInTx, GetLabelsForIssuesInTx) when the rows iterator finishes in an error state — connection terminated mid-stream, driver I/O error, or ctx cancelled after the last successful Scan.","commonSituations":"Dolt server restart or load-balancer idle timeout severing a long result stream; caller's request deadline expiring mid-iteration; flaky network to a remote Dolt backend during bulk operations.","solutions":["Unwrap the driver error; if it is context.DeadlineExceeded, increase the deadline or shrink the ID batch","Retry the whole partition in a fresh transaction — it is read-only and safe to repeat","Check network stability to the Dolt server (keepalives, proxy timeouts) if this recurs","Confirm the caller is not cancelling the context early (e.g. an HTTP client timeout shorter than the query time)"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(ctx, 2*time.Second) // too tight\nwispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)\n// after\nctx, cancel := context.WithTimeout(ctx, 30*time.Second) // sized for batch\nwispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)\nif err != nil { return retryPartition(context.Background(), tx, ids) }","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil { return err }\nif deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < 5*time.Second {\n\treturn errors.New(\"deadline too tight for wisp partition\")\n}","typeGuard":null,"tryCatchPattern":"wispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)\nif err != nil {\n\tif strings.Contains(err.Error(), \"rows:\") && errors.Is(err, context.DeadlineExceeded) {\n\t\treturn retryPartitionWithLargerDeadline(ctx, ids)\n\t}\n\treturn err\n}","preventionTips":["Give partition calls generous deadlines proportional to batch size","Enable TCP keepalives / raise proxy idle timeouts for Dolt connections","Avoid cancelling parent contexts mid-query","Retry the whole read-only partition — it is safe to repeat"],"tags":["database","wisp-routing","rows-iteration"],"backgroundTag":"db-rows-iteration-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}