{"record":{"id":"75384f016bbf5a1f","repo":"charmbracelet/crush","slug":"get-usage-by-model-w","errorCode":null,"errorMessage":"get usage by model: %w","messagePattern":"get usage by model: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/stats.go","lineNumber":581,"sourceCode":"\t\treturn nil, fmt.Errorf(\"get usage by day: %w\", err)\n\t}\n\tfor _, d := range dailyUsage {\n\t\tprompt := nullFloat64ToInt64(d.PromptTokens)\n\t\tcompletion := nullFloat64ToInt64(d.CompletionTokens)\n\t\tstats.UsageByDay = append(stats.UsageByDay, DailyUsage{\n\t\t\tDay:              fmt.Sprintf(\"%v\", d.Day),\n\t\t\tPromptTokens:     prompt,\n\t\t\tCompletionTokens: completion,\n\t\t\tTotalTokens:      prompt + completion,\n\t\t\tCost:             d.Cost.Float64,\n\t\t\tSessionCount:     d.SessionCount,\n\t\t})\n\t}\n\n\t// Usage by model.\n\tmodelUsage, err := queries.GetUsageByModel(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get usage by model: %w\", err)\n\t}\n\tfor _, m := range modelUsage {\n\t\tstats.UsageByModel = append(stats.UsageByModel, ModelUsage{\n\t\t\tModel:        m.Model,\n\t\t\tProvider:     m.Provider,\n\t\t\tMessageCount: m.MessageCount,\n\t\t})\n\t}\n\n\t// Usage by hour.\n\thourlyUsage, err := queries.GetUsageByHour(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get usage by hour: %w\", err)\n\t}\n\tfor _, h := range hourlyUsage {\n\t\tstats.UsageByHour = append(stats.UsageByHour, HourlyUsage{\n\t\t\tHour:         int(h.Hour),\n\t\t\tSessionCount: h.SessionCount,","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/stats.go#L563-L599","documentation":"gatherStats aggregates token/message usage per model via queries.GetUsageByModel and wraps any failure with this message. It signals the model-usage SQL query could not run against the session database.","triggerScenarios":"GetUsageByModel fails: SQLite DB locked by another process, unreadable/corrupt database file, or schema incompatibility (older/newer crush writing the DB).","commonSituations":"Two crush instances writing sessions concurrently; crash-corrupted DB; running stats from a different user than the one who owns .crush; downgraded binary against a newer schema.","solutions":["Close other crush processes and rerun `crush stats`","Check file permissions on the data dir/DB and fix with chown/chmod","Validate the DB with `PRAGMA integrity_check` and restore or regenerate if corrupt","Align the crush binary version with the database schema (reinstall the expected version)"],"exampleFix":"// before\nsudo crush stats  # creates root-owned DB access issues\n// after\nchown -R $USER .crush && crush stats  # run as the owning user, not via sudo","handlingStrategy":"retry","validationCode":"db=\"${CRUSH_DATA_DIR:-.crush}/crush.db\"\n[ -O \"$db\" ] || { echo \"You do not own $db (run chown $USER)\" >&2; exit 1; }\npgrep -f crush >/dev/null && { echo \"Another crush process may hold the DB lock\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"if err := gatherStats(ctx, dbPath); err != nil {\n    if strings.Contains(err.Error(), \"get usage by model\") && strings.Contains(err.Error(), \"locked\") {\n        // wait for the writer and retry\n        time.Sleep(2 * time.Second)\n        return gatherStats(ctx, dbPath)\n    }\n    return err\n}","preventionTips":["Run crush as one consistent user so DB ownership stays with you","Don't launch multiple crush instances that write the same data dir","After any sudo use of crush, chown the data dir back","Keep the DB off NFS","Verify schema version matches your binary before running stats"],"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"}