{"record":{"id":"8415c1c6b21aa909","repo":"gastownhall/beads","slug":"iterate-dependents-from-s-w","errorCode":null,"errorMessage":"iterate dependents from %s: %w","messagePattern":"iterate dependents from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete_role.go","lineNumber":225,"sourceCode":"\t\t\t\treturn nil, fmt.Errorf(\"query dependents from %s: %w\", depTable, err)\n\t\t\t}\n\t\t\tfor rows.Next() {\n\t\t\t\tvar target, dependent string\n\t\t\t\tif err := rows.Scan(&target, &dependent); err != nil {\n\t\t\t\t\t_ = rows.Close()\n\t\t\t\t\treturn nil, fmt.Errorf(\"scan dependent: %w\", err)\n\t\t\t\t}\n\t\t\t\tif idSet[dependent] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif bySource[target] == nil {\n\t\t\t\t\tbySource[target] = make(map[string]bool)\n\t\t\t\t}\n\t\t\t\tbySource[target][dependent] = true\n\t\t\t}\n\t\t\t_ = rows.Close()\n\t\t\tif err := rows.Err(); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"iterate dependents from %s: %w\", depTable, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tout := make(map[string][]string, len(bySource))\n\tfor target, dependents := range bySource {\n\t\tout[target] = workapi.SortedDeleteIDs(dependents)\n\t}\n\treturn out, nil\n}\n\n// deleteNeighborsInTx hydrates the SURVIVING rows joined to the deletion set\n// by a dependency edge in either direction — the rows whose text the deletion\n// rewrites.\n//\n// One query per plane over the whole set, so a `--from-file` batch costs two\n// queries rather than two per deleted id.\n//","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete_role.go#L207-L243","documentation":"After consuming all rows from a dependents query, rows.Err() reported a failure that only becomes visible at the end of iteration — typically a dropped connection or cancelled context mid-result-set. The wrapper names the table whose iteration broke. All successfully scanned rows before the failure are discarded and the delete aborts.","triggerScenarios":"ExternalDependentsBySourceInTx finishes the rows.Next() loop over dependencies/wisp_dependencies and rows.Err() is non-nil — network interruption, server-side abort, or context cancellation during result streaming.","commonSituations":"Flaky network to a remote Dolt/MySQL server; context deadline expiring mid-iteration on a very large batch; server killing a long-running query.","solutions":["Retry the delete with a fresh transaction; the operation is read-only at this stage and idempotent","Increase the context timeout for large batches","Check network stability and server logs for aborted connections","Reduce deleteBatchSize so each result set streams quickly","Run deletes close to the database rather than over a WAN"],"exampleFix":"// before\nctx := context.Background()\nstore.Delete(ctx, req)\n// after\ncctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\ndefer cancel()\nstore.Delete(cctx, req)","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil { return err } // ensure budget before large batch","typeGuard":null,"tryCatchPattern":"err := store.Delete(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"iterate dependents from\") {\n    // deferred rows.Err(); retry with fresh tx and longer timeout\n}","preventionTips":["Use contexts with timeouts sized to batch size","Prefer LAN-local databases for large deletes","Watch server logs for aborted connections","Split large id lists into smaller delete batches"],"tags":["database","network","iteration","wrapper-error"],"backgroundTag":"db-connection-dropped","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}