{"record":{"id":"c9667294feab942f","repo":"gastownhall/beads","slug":"get-dependency-counts-blockers-from-s-w","errorCode":null,"errorMessage":"get dependency counts (blockers from %s): %w","messagePattern":"get dependency counts \\(blockers from (.+?)\\): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_queries.go","lineNumber":474,"sourceCode":"\t\tfor i, id := range batch {\n\t\t\tplaceholders[i] = \"?\"\n\t\t\targs[i] = id\n\t\t}\n\t\tinClause := strings.Join(placeholders, \",\")\n\n\t\tfor _, depTable := range depTables {\n\t\t\t//nolint:gosec // G201: depTable is hardcoded and inClause contains only ? placeholders.\n\t\t\tdepRows, err := tx.QueryContext(ctx, fmt.Sprintf(`\n\t\t\t\tSELECT issue_id, COUNT(*) as cnt\n\t\t\t\tFROM %s\n\t\t\t\tWHERE issue_id IN (%s) AND type = 'blocks'\n\t\t\t\tGROUP BY issue_id\n\t\t\t`, depTable, inClause), args...)\n\t\t\tif err != nil {\n\t\t\t\tif optionalBlockedTable(depTable) && isTableNotExistError(err) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts (blockers from %s): %w\", depTable, err)\n\t\t\t}\n\t\t\tfor depRows.Next() {\n\t\t\t\tvar id string\n\t\t\t\tvar cnt int\n\t\t\t\tif err := depRows.Scan(&id, &cnt); err != nil {\n\t\t\t\t\t_ = depRows.Close()\n\t\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts: scan blocker: %w\", err)\n\t\t\t\t}\n\t\t\t\tif c, ok := result[id]; ok {\n\t\t\t\t\tc.DependencyCount += cnt\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ = depRows.Close()\n\t\t\tif err := depRows.Err(); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts: blocker rows: %w\", err)\n\t\t\t}\n\n\t\t\t//nolint:gosec // G201: depTable is hardcoded and inClause contains only ? placeholders.","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_queries.go#L456-L492","documentation":"This error wraps the failure of the per-table 'blocks' count query in GetDependencyCountsInTx — a GROUP BY issue_id COUNT over a dependency table filtered to type='blocks'. It is returned only when the table is expected to exist (optional wisp table absence is skipped via isTableNotExistError), so it signals a real query failure on a present table.","triggerScenarios":"Calling GetDependencyCountsInTx/HydrateReadyRowInTx when 'dependencies' (or a present 'wisp_dependencies') rejects the blockers query: schema drift (missing issue_id/type columns), SQL dialect incompatibility, context cancellation, connection failure, or lock contention.","commonSituations":"Schema drift after an aborted migration; remote database network issues during ready-row hydration; another process holding a long lock; driver version mismatch with the SQL used in the counts query.","solutions":["Inspect the wrapped driver error to classify the root cause (missing column vs connection vs lock).","Run migrations so the table has issue_id and type columns with supporting indexes for the GROUP BY.","Retry on transient errors; investigate lock holders if 'lock wait timeout' appears.","Verify the driver supports the exact SQL dialect used by the counts query.","If it happens only for wisp_dependencies, the missing-table skip failed — check isTableNotExistError coverage for your driver."],"exampleFix":"// before: wisp table missing but error not recognized, so counts abort\n// after: ensure migrations create the table so classification never matters\nCREATE TABLE IF NOT EXISTS wisp_dependencies (id VARCHAR(64) PRIMARY KEY, issue_id VARCHAR(64) NOT NULL, ...);","handlingStrategy":"validation","validationCode":"// Confirm the blockers query preconditions: columns exist and table is present\nrows, err := tx.QueryContext(ctx, \"SELECT issue_id, type FROM dependencies WHERE type = 'blocks' LIMIT 1\")\nif err != nil { /* migration incomplete; hydration counts will fail */ }","typeGuard":"func blockersQueryReady(ctx context.Context, tx DBTX, table string) bool {\n    var id string\n    err := tx.QueryRowContext(ctx, \"SELECT issue_id FROM \"+table+\" WHERE type = 'blocks' LIMIT 1\").Scan(&id)\n    return err == nil || isTableNotExistError(err)\n}","tryCatchPattern":"counts, err := GetDependencyCountsInTx(ctx, tx, issueIDs)\nif err != nil && strings.Contains(err.Error(), \"blockers from \") {\n    if isTableNotExistError(err) { /* wisp table edge case: verify optional-table skip */ }\n    else { /* schema or connectivity problem: migrate/retry accordingly */ }\n}","preventionTips":["Keep issue_id and type columns indexed for the GROUP BY query","Complete migrations fully; abort-on-failure migrations prevent drift","Serialize long writes to avoid lock contention during hydration","Test hydration against your exact driver version before upgrades"],"tags":["database","sql","dependency-counts","storage","hydration"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}