{"record":{"id":"b151b8e8a66bcbc6","repo":"gastownhall/beads","slug":"scan-issue-counts-w","errorCode":null,"errorMessage":"scan issue counts: %w","messagePattern":"scan issue counts: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/statistics.go","lineNumber":32,"sourceCode":"func ScanIssueCountsInTx(ctx context.Context, tx DBTX, stats *types.Statistics) error {\n\tif err := tx.QueryRowContext(ctx, `\n\t\tSELECT\n\t\t\tCOUNT(*) AS total,\n\t\t\tCOALESCE(SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END), 0),\n\t\t\tCOALESCE(SUM(CASE WHEN status = 'in_progress' THEN 1 ELSE 0 END), 0),\n\t\t\tCOALESCE(SUM(CASE WHEN status = 'closed' THEN 1 ELSE 0 END), 0),\n\t\t\tCOALESCE(SUM(CASE WHEN status = 'deferred' THEN 1 ELSE 0 END), 0),\n\t\t\tCOALESCE(SUM(CASE WHEN pinned = 1 THEN 1 ELSE 0 END), 0)\n\t\tFROM issues\n\t`).Scan(\n\t\t&stats.TotalIssues,\n\t\t&stats.OpenIssues,\n\t\t&stats.InProgressIssues,\n\t\t&stats.ClosedIssues,\n\t\t&stats.DeferredIssues,\n\t\t&stats.PinnedIssues,\n\t); err != nil {\n\t\treturn fmt.Errorf(\"scan issue counts: %w\", err)\n\t}\n\treturn nil\n}\n\n// GetStatisticsInTx computes the full summary statistics (counts + blocked + ready)\n// in one transaction, using only the normal issues table — no version-control state —\n// so it is portable across every SQL backend. Behaviorally identical to the Dolt and\n// embedded-Dolt implementations: ScanIssueCountsInTx for the status counts, a direct\n// blocked count (the is_blocked flag is maintained in-tx by the shared layer), then\n// ReadyIssues = OpenIssues - BlockedIssues clamped at zero.\nfunc GetStatisticsInTx(ctx context.Context, tx DBTX) (*types.Statistics, error) {\n\tstats := &types.Statistics{}\n\tif err := ScanIssueCountsInTx(ctx, tx, stats); err != nil {\n\t\treturn nil, err\n\t}\n\tvar blocked int\n\tif err := tx.QueryRowContext(ctx, `\n\t\tSELECT COUNT(*) FROM issues","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/statistics.go#L14-L50","documentation":"ScanIssueCountsInTx runs a single-row aggregate SELECT (total/open/in_progress/closed/deferred/pinned counts) and scans six ints; this wraps any Scan failure. A single-row aggregate always returns one row, so failure usually means the row was NULL (empty/corrupt table), the query failed, or the destination types mismatch.","triggerScenarios":"Calling GetStatisticsInTx (or ScanIssueCountsInTx directly) when the issues table doesn't exist, the connection is broken, the context is cancelled, or the database returns NULLs the int destinations can't absorb.","commonSituations":"Pointing bd at an empty or uninitialized database file; running stats before migrations created the issues table; a Dolt server outage while running `bd stats`; schema drift after a version upgrade.","solutions":["Check the wrapped error text for 'no such table' — run the schema initialization/migration before calling statistics.","Verify the database connection is alive and the tx wasn't already rolled back.","If COUNT(*) returning NULL is the cause, confirm the issues table exists and has rows; an empty initialized table still returns one row of zeros, so NULL indicates schema corruption.","Upgrade to a matching schema version if the error appeared after a beads version change."],"exampleFix":"// before\nstats, err := GetStatisticsInTx(ctx, tx) // fails: issues table missing\n// after\nif err := store.Initialize(ctx); err != nil { return err } // ensure schema\nstats, err := GetStatisticsInTx(ctx, tx)","handlingStrategy":"validation","validationCode":"// verify schema readiness before asking for statistics\nrows, err := tx.QueryContext(ctx, `SHOW TABLES LIKE 'issues'`)\nif err != nil || !rows.Next() { return fmt.Errorf(\"issues table missing; initialize database\") }","typeGuard":null,"tryCatchPattern":"stats, err := issueops.GetStatisticsInTx(ctx, tx)\nif err != nil {\n    if strings.Contains(err.Error(), \"scan issue counts\") {\n        return fmt.Errorf(\"statistics unavailable; check schema and connection: %w\", err)\n    }\n    return err\n}","preventionTips":["Initialize/migrate the database before calling statistics","Point bd at the correct database path (config mistakes are common)","Check connection health when stats follow other long operations","Pin schema version when upgrading beads"],"tags":["go","sql","scan","statistics"],"backgroundTag":"sql-scan-null-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}