{"record":{"id":"99b7e6401ee482fa","repo":"gastownhall/beads","slug":"get-dependency-records-from-s-w","errorCode":null,"errorMessage":"get dependency records from %s: %w","messagePattern":"get dependency records from (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_queries.go","lineNumber":120,"sourceCode":"func getDependencyRecordsIntoFromTable(ctx context.Context, tx DBTX, depTable string, ids []string, result map[string][]*types.Dependency) error {\n\tfor start := 0; start < len(ids); start += queryBatchSize {\n\t\tend := start + queryBatchSize\n\t\tif end > len(ids) {\n\t\t\tend = len(ids)\n\t\t}\n\t\tbatch := ids[start:end]\n\t\tplaceholders := make([]string, len(batch))\n\t\targs := make([]any, len(batch))\n\t\tfor i, id := range batch {\n\t\t\tplaceholders[i] = \"?\"\n\t\t\targs[i] = id\n\t\t}\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(\n\t\t\t`SELECT issue_id, %s AS depends_on_id, type, created_at, created_by, metadata, thread_id\n\t\t\t FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, depends_on_id, type, id`,\n\t\t\tDepTargetExpr, depTable, strings.Join(placeholders, \",\")), args...)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"get dependency records from %s: %w\", depTable, err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tdep, scanErr := scanDependencyRow(rows)\n\t\t\tif scanErr != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn fmt.Errorf(\"get dependency records: scan: %w\", scanErr)\n\t\t\t}\n\t\t\tresult[dep.IssueID] = append(result[dep.IssueID], dep)\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn fmt.Errorf(\"get dependency records: rows: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetDependentRecordsForIssuesInTx returns raw dependency rows keyed by TARGET","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_queries.go#L102-L138","documentation":"getDependencyRecordsIntoFromTable wraps the error from tx.QueryContext when fetching dependency records for a specific set of issue IDs from a given table. The per-issue variant used by GetDependencyRecordsForIssuesInTx routes each ID to issues_dependencies or wisp_dependencies; a query failure on either table surfaces here with the table name and driver error via %w.","triggerScenarios":"tx.QueryContext failing in GetDependencyRecordsForIssuesInTx / GetDependencyRecordsForIssuesFromTableInTx — connection loss, driver error building/executing the IN (...) SELECT, or too many placeholders for the driver with a very large ID list.","commonSituations":"Passing thousands of issue IDs at once producing an oversized IN clause; connection dropped mid-transaction; SQL prepared-statement limits exceeded on some drivers.","solutions":["Unwrap the %w cause to identify the driver error and address it (connectivity, statement limits).","If the ID list is very large, chunk it into batches (e.g. a few hundred IDs per call) to keep the IN clause within driver limits.","Retry the operation in a fresh transaction; it is a read and safe to repeat."],"exampleFix":"// before: one huge IN clause\nrecords, err := GetDependencyRecordsForIssuesInTx(ctx, tx, allThousandIDs)\n\n// after: batch the IDs\nfor chunk := range slices.Chunk(allThousandIDs, 500) {\n    records, err := GetDependencyRecordsForIssuesInTx(ctx, tx, chunk)\n    ...\n}","handlingStrategy":"validation","validationCode":"const maxBatch = 500\nif len(issueIDs) > maxBatch {\n    return fmt.Errorf(\"batch of %d issue IDs exceeds %d; chunk the call\", len(issueIDs), maxBatch)\n}\nfor _, id := range issueIDs {\n    if id == \"\" { return fmt.Errorf(\"empty issue ID in batch\") }\n}","typeGuard":null,"tryCatchPattern":"recs, err := GetDependencyRecordsForIssuesInTx(ctx, tx, ids)\nif err != nil {\n    var drv driver.Error\n    if errors.As(err, &drv) { /* inspect driver cause: statement limits, connection */ }\n    return err\n}","preventionTips":["Chunk large ID lists (a few hundred per call) to stay within driver placeholder limits.","Filter out empty/invalid IDs before building the IN clause.","Retry within a fresh transaction on transient failures; the read is idempotent."],"tags":["go","database","sql","query-failed","batching"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}