{"record":{"id":"5e3c68a515c7a28b","repo":"gastownhall/beads","slug":"count-dependent-records-from-s-w","errorCode":null,"errorMessage":"count dependent records from %s: %w","messagePattern":"count dependent records from (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_queries.go","lineNumber":425,"sourceCode":"\t\twispWhere, durableWhere)\n\tvar n int\n\tif err := tx.QueryRowContext(ctx, query, args...).Scan(&n); err != nil {\n\t\treturn 0, fmt.Errorf(\"count wisp-only dependent records: %w\", err)\n\t}\n\treturn n, nil\n}\n\n//nolint:gosec // G201: depTable is a hardcoded constant; targetID/depType are bound as parameters.\nfunc countDependentRecordsFromTable(ctx context.Context, tx DBTX, depTable, targetID, depType string) (int, error) {\n\tquery := fmt.Sprintf(\"SELECT COUNT(*) FROM %s WHERE %s\", depTable, depTargetEqualsOr())\n\targs := []any{targetID, targetID, targetID}\n\tif depType != \"\" {\n\t\tquery += \" AND type = ?\"\n\t\targs = append(args, depType)\n\t}\n\tvar n int\n\tif err := tx.QueryRowContext(ctx, query, args...).Scan(&n); err != nil {\n\t\treturn 0, fmt.Errorf(\"count dependent records from %s: %w\", depTable, err)\n\t}\n\treturn n, nil\n}\n\nfunc GetDependencyCountsInTx(ctx context.Context, tx DBTX, issueIDs []string) (map[string]*types.DependencyCounts, error) {\n\tif len(issueIDs) == 0 {\n\t\treturn make(map[string]*types.DependencyCounts), nil\n\t}\n\n\tresult := make(map[string]*types.DependencyCounts)\n\tfor _, id := range issueIDs {\n\t\tresult[id] = &types.DependencyCounts{}\n\t}\n\n\tdepTables := []string{\"dependencies\", \"wisp_dependencies\"}\n\tif empty, probeErr := wispsTableEmptyOrMissingInTx(ctx, tx); probeErr != nil {\n\t\treturn nil, fmt.Errorf(\"get dependency counts: probe: %w\", probeErr)\n\t} else if empty {","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_queries.go#L407-L443","documentation":"This error wraps the COUNT(*) QueryRowContext failure in countDependentRecordsFromTable, which counts inbound dependency rows for a target in a single table ('dependencies' or 'wisp_dependencies'), optionally filtered by dependency type. It means the count query failed to execute or its result could not be scanned into an int.","triggerScenarios":"Calling CountDependentRecordsInTx when the table is missing (pre-migration schema), the target predicate SQL fails on the driver, context cancellation, connection drop, or COUNT result scan failure.","commonSituations":"Older schema without the dependency table; remote database connection issues; driver incompatibility with the depTargetEqualsOr OR predicate; locked database during a long write from another process.","solutions":["Read the wrapped driver error to classify: missing table vs connection vs lock timeout.","Run schema migrations so the dependency tables exist with the target indexes (idx_dep_*).","Retry on transient errors; consider serializing heavy writers if lock contention is the cause.","Confirm DB user read permissions on the dependency tables.","If scanning fails, check that COUNT returns an integer type the driver maps to int."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-check table existence and connectivity\nvar n int\nif err := tx.QueryRowContext(ctx, \"SELECT COUNT(*) FROM dependencies LIMIT 1\").Scan(&n); err != nil {\n    return fmt.Errorf(\"dependencies table not countable: %w\", err)\n}","typeGuard":"func tableExists(ctx context.Context, tx DBTX, table string) bool {\n    var one int\n    err := tx.QueryRowContext(ctx, \"SELECT 1 FROM \"+table+\" LIMIT 1\").Scan(&one)\n    return err == nil\n}","tryCatchPattern":"n, err := CountDependentRecordsInTx(ctx, tx, targetID, depType)\nif err != nil {\n    switch {\n    case isTableNotExistError(err): // migrate or return 0\n    case isLockTimeout(err): // retry after backing off\n    case isTransientNetError(err): // retry\n    default: return err\n    }\n}","preventionTips":["Apply migrations before read-heavy operations","Avoid concurrent long writes that lock dependency tables during counts","Verify integer COUNT mapping with your driver","Keep context deadlines generous for large tables"],"tags":["database","sql","count-query","storage"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}