{"record":{"id":"fa067b98ff07f9f0","repo":"charmbracelet/crush","slug":"get-total-stats-w","errorCode":null,"errorMessage":"get total stats: %w","messagePattern":"get total stats: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/stats.go","lineNumber":547,"sourceCode":"\t})\n\tsort.Slice(merged.ToolUsage, func(i, j int) bool {\n\t\treturn merged.ToolUsage[i].CallCount > merged.ToolUsage[j].CallCount\n\t})\n\n\treturn merged\n}\n\nfunc gatherStats(ctx context.Context, conn *sql.DB) (*Stats, error) {\n\tqueries := db.New(conn)\n\n\tstats := &Stats{\n\t\tGeneratedAt: time.Now().UTC(),\n\t}\n\n\t// Total stats.\n\ttotal, err := queries.GetTotalStats(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get total stats: %w\", err)\n\t}\n\tstats.Total = TotalStats{\n\t\tTotalSessions:         total.TotalSessions,\n\t\tTotalPromptTokens:     toInt64(total.TotalPromptTokens),\n\t\tTotalCompletionTokens: toInt64(total.TotalCompletionTokens),\n\t\tTotalTokens:           toInt64(total.TotalPromptTokens) + toInt64(total.TotalCompletionTokens),\n\t\tTotalCost:             toFloat64(total.TotalCost),\n\t\tTotalMessages:         toInt64(total.TotalMessages),\n\t\tAvgTokensPerSession:   toFloat64(total.AvgTokensPerSession),\n\t\tAvgMessagesPerSession: toFloat64(total.AvgMessagesPerSession),\n\t}\n\n\t// Usage by day.\n\tdailyUsage, err := queries.GetUsageByDay(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get usage by day: %w\", err)\n\t}\n\tfor _, d := range dailyUsage {","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/stats.go#L529-L565","documentation":"gatherStats runs SQLC-generated queries against the session SQLite database. This error wraps a failure from queries.GetTotalStats, the aggregate totals query (sessions, tokens, message averages). Because the DB is opened with a busy timeout, the most common failure is the database file being locked or unavailable, or a schema mismatch after an upgrade.","triggerScenarios":"GetTotalStats fails: the SQLite file cannot be read (permissions, missing file), the DB is locked by another crush process, disk I/O error, or the schema is from an incompatible version (missing tables/columns).","commonSituations":"Running `crush stats` while another crush instance holds a write lock; pointing --data-dir at a directory without a crush.db; downgrading crush so stats tables no longer match; NFS-mounted home dirs where SQLite locking is unreliable.","solutions":["Ensure no other crush process is running that holds the DB lock, then retry","Verify the DB file exists in the data dir (default .crush/crush.db) and is readable by the current user","If the schema may be stale or incompatible, remove/rename the DB (losing stats) or upgrade to a matching crush version","Move the data dir off NFS to a local filesystem if locking errors persist"],"exampleFix":"// before\ncrush stats  # Error: database is locked\n// after\npkill -f 'crush' && crush stats   # or copy .crush locally if it sits on NFS","handlingStrategy":"retry","validationCode":"# Pre-check DB accessibility before running stats\ndb=\"${CRUSH_DATA_DIR:-.crush}/crush.db\"\n[ -f \"$db\" ] || { echo \"DB missing: $db\" >&2; exit 1; }\n[ -r \"$db\" ] || { echo \"DB unreadable: $db\" >&2; exit 1; }\nsqlite3 \"$db\" 'PRAGMA integrity_check;' | grep -q '^ok' || { echo \"DB corrupt\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"if err := gatherStats(ctx, dbPath); err != nil {\n    var lockedErr sqlite3.Error\n    if errors.As(err, &lockedErr) && errors.Is(err, sqlite3.ErrLocked) || strings.Contains(err.Error(), \"locked\") {\n        time.Sleep(500 * time.Millisecond)\n        return gatherStats(ctx, dbPath) // retry after writer releases lock\n    }\n    return err\n}","preventionTips":["Don't run `crush stats` while a crush session is actively writing","Keep the data dir on a local filesystem, not NFS (SQLite locking is unreliable there)","Avoid sudo runs that change DB file ownership","Back up crush.db before upgrading or deleting it","Check `PRAGMA integrity_check` after any crash"],"tags":["sqlite","database","sql","cli"],"backgroundTag":"sqlite-database-locked","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}