{"record":{"id":"6c26bda486d77b55","repo":"gastownhall/beads","slug":"wisp-id-set-rows-w","errorCode":null,"errorMessage":"wisp id set: rows: %w","messagePattern":"wisp id set: rows: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/wisp_routing.go","lineNumber":104,"sourceCode":"\t\t\tplaceholders[i] = \"?\"\n\t\t\targs[i] = id\n\t\t}\n\t\tq := fmt.Sprintf(\"SELECT id FROM wisps WHERE id IN (%s)\", strings.Join(placeholders, \",\"))\n\t\trows, err := tx.QueryContext(ctx, q, args...)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"wisp id set: %w\", err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar id string\n\t\t\tif err := rows.Scan(&id); err != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn nil, fmt.Errorf(\"wisp id set: scan: %w\", err)\n\t\t\t}\n\t\t\tset[id] = struct{}{}\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"wisp id set: rows: %w\", err)\n\t\t}\n\t}\n\treturn set, nil\n}\n\n// partitionByWispSet splits ids into (wispIDs, permIDs) using the provided\n// wisp-id set. If wispSet is nil the caller must populate it first via\n// WispIDSetInTx; this helper does no I/O.\nfunc partitionByWispSet(ids []string, wispSet map[string]struct{}) (wispIDs, permIDs []string) {\n\tfor _, id := range ids {\n\t\tif _, isWisp := wispSet[id]; isWisp {\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\n}","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/wisp_routing.go#L86-L122","documentation":"WispIDSetInTx issues a batched `SELECT id FROM wisps WHERE id IN (...)` and calls rows.Err() after draining each batch. This error wraps a rows-iteration failure (connection drop, context cancellation mid-scan, driver decode error) after rows were successfully opened but the iteration itself reported an error via rows.Err(). It means the wisp-ID set could not be built reliably, so the caller must not trust a partial partition.","triggerScenarios":"Calling WispIDSetInTx (directly or via ReconcileChildCounters, DeleteInTx, GetIssuesByIDsInTx, ExecuteAddDependencies) when the underlying database connection fails or the context is cancelled while iterating result rows of the wisps-table IN query.","commonSituations":"Dolt server connection dropped mid-query (network blip to remote Dolt); caller's context deadline exceeded during a large batch partition (GH#3414 WAN-latency scenario); driver-level row decode failure after schema drift.","solutions":["Check the wrapped driver error to determine root cause: network error → reconnect; context.DeadlineExceeded → raise the timeout or reduce the batch size","Retry the operation with a fresh transaction — Dolt MVCC makes the set consistent per tx, so a clean retry is safe","Verify connectivity to the Dolt server (bd dolt ping / connection settings) if errors are persistent","Reduce queryBatchSize input volume so each IN-query chunk completes well inside the context deadline"],"exampleFix":"// before\nset, err := issueops.WispIDSetInTx(ctx, tx, ids)\nif err != nil { return err }\n// after\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\nset, err := issueops.WispIDSetInTx(ctx, tx, ids)\nif err != nil {\n\tif errors.Is(err, context.DeadlineExceeded) { return retryWithFreshTx(ctx, ids) }\n\treturn fmt.Errorf(\"partition wisps: %w\", err)\n}","handlingStrategy":"retry","validationCode":"if len(ids) == 0 { return nil } // never triggers a query\nif err := ctx.Err(); err != nil { return fmt.Errorf(\"context already done: %w\", err) }","typeGuard":null,"tryCatchPattern":"set, err := issueops.WispIDSetInTx(ctx, tx, ids)\nif err != nil {\n\tvar netErr net.Error\n\tif errors.As(err, &netErr) || errors.Is(err, context.DeadlineExceeded) {\n\t\t// retry with fresh tx and larger deadline\n\t}\n\treturn err\n}","preventionTips":["Size context deadlines for the batch volume, especially over WAN to remote Dolt","Pass a non-cancelled context; avoid propagating short HTTP timeouts into storage calls","Keep batches within queryBatchSize so each IN-query chunk is small","Monitor connection stability to the Dolt server"],"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"}