{"record":{"id":"186fb39f56225d97","repo":"charmbracelet/crush","slug":"get-recent-activity-w","errorCode":null,"errorMessage":"get recent activity: %w","messagePattern":"get recent activity: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/stats.go","lineNumber":621,"sourceCode":"\t// Usage by day of week.\n\tdowUsage, err := queries.GetUsageByDayOfWeek(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get usage by day of week: %w\", err)\n\t}\n\tfor _, d := range dowUsage {\n\t\tstats.UsageByDayOfWeek = append(stats.UsageByDayOfWeek, DayOfWeekUsage{\n\t\t\tDayOfWeek:        int(d.DayOfWeek),\n\t\t\tDayName:          dayNames[int(d.DayOfWeek)],\n\t\t\tSessionCount:     d.SessionCount,\n\t\t\tPromptTokens:     nullFloat64ToInt64(d.PromptTokens),\n\t\t\tCompletionTokens: nullFloat64ToInt64(d.CompletionTokens),\n\t\t})\n\t}\n\n\t// Recent activity (last 30 days).\n\trecent, err := queries.GetRecentActivity(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get recent activity: %w\", err)\n\t}\n\tfor _, r := range recent {\n\t\tstats.RecentActivity = append(stats.RecentActivity, DailyActivity{\n\t\t\tDay:          fmt.Sprintf(\"%v\", r.Day),\n\t\t\tSessionCount: r.SessionCount,\n\t\t\tTotalTokens:  nullFloat64ToInt64(r.TotalTokens),\n\t\t\tCost:         r.Cost.Float64,\n\t\t})\n\t}\n\n\t// Average response time.\n\tavgResp, err := queries.GetAverageResponseTime(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get average response time: %w\", err)\n\t}\n\tstats.AvgResponseTimeMs = toFloat64(avgResp) * 1000\n\n\t// Tool usage.","sourceCodeStart":603,"sourceCodeEnd":639,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/stats.go#L603-L639","documentation":"gatherStats fetches the last-30-days activity via queries.GetRecentActivity and wraps any SQL failure with this message. As with the other gatherStats errors, the wrapped cause is almost always SQLite access (lock, permissions, corruption) or schema incompatibility.","triggerScenarios":"GetRecentActivity fails: DB locked by a concurrent crush session, database file unreadable/corrupt, or schema drift so the prepared query fails.","commonSituations":"Running stats while a crush session is actively writing; disk-full or I/O errors corrupting the DB; downgraded/older binary vs newer schema; DB on NFS with broken locking.","solutions":["Close concurrent crush sessions and rerun `crush stats`","Check disk space (`df -h`) and DB integrity (`PRAGMA integrity_check`)","Restore the DB from backup or delete it to regenerate","Point --data-dir at a local filesystem path"],"exampleFix":"// before\ncrush stats  # disk full, I/O error on DB write\n// after\ndf -h  # free space, then rerun crush stats","handlingStrategy":"retry","validationCode":"db=\"${CRUSH_DATA_DIR:-.crush}/crush.db\"\n[ \"$(df -h \"$(dirname \"$db\")\" | awk 'NR==2{print $4}')\" != \"0\" ] && sqlite3 \"$db\" 'PRAGMA integrity_check;' | grep -q ok && echo \"ready\" || echo \"DB or disk problem\"","typeGuard":null,"tryCatchPattern":"if err := gatherStats(ctx, dbPath); err != nil {\n    if strings.Contains(err.Error(), \"get recent activity\") {\n        if backoff(ctx) == nil { // bounded retry for transient I/O/lock\n            return gatherStats(ctx, dbPath)\n        }\n    }\n    return err\n}","preventionTips":["Keep free disk space where crush.db lives","Close active crush sessions before running stats","Validate the DB with integrity_check after any crash","Store the DB on local storage with reliable locking","Back up crush.db before upgrades or deletion"],"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"}