charmbracelet/crush · error

get total stats: %w

Error message

get total stats: %w

What it means

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.

Source

Thrown at internal/cmd/stats.go:547

	})
	sort.Slice(merged.ToolUsage, func(i, j int) bool {
		return merged.ToolUsage[i].CallCount > merged.ToolUsage[j].CallCount
	})

	return merged
}

func gatherStats(ctx context.Context, conn *sql.DB) (*Stats, error) {
	queries := db.New(conn)

	stats := &Stats{
		GeneratedAt: time.Now().UTC(),
	}

	// Total stats.
	total, err := queries.GetTotalStats(ctx)
	if err != nil {
		return nil, fmt.Errorf("get total stats: %w", err)
	}
	stats.Total = TotalStats{
		TotalSessions:         total.TotalSessions,
		TotalPromptTokens:     toInt64(total.TotalPromptTokens),
		TotalCompletionTokens: toInt64(total.TotalCompletionTokens),
		TotalTokens:           toInt64(total.TotalPromptTokens) + toInt64(total.TotalCompletionTokens),
		TotalCost:             toFloat64(total.TotalCost),
		TotalMessages:         toInt64(total.TotalMessages),
		AvgTokensPerSession:   toFloat64(total.AvgTokensPerSession),
		AvgMessagesPerSession: toFloat64(total.AvgMessagesPerSession),
	}

	// Usage by day.
	dailyUsage, err := queries.GetUsageByDay(ctx)
	if err != nil {
		return nil, fmt.Errorf("get usage by day: %w", err)
	}
	for _, d := range dailyUsage {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Ensure no other crush process is running that holds the DB lock, then retry
  2. Verify the DB file exists in the data dir (default .crush/crush.db) and is readable by the current user
  3. If the schema may be stale or incompatible, remove/rename the DB (losing stats) or upgrade to a matching crush version
  4. Move the data dir off NFS to a local filesystem if locking errors persist

Example fix

// before
crush stats  # Error: database is locked
// after
pkill -f 'crush' && crush stats   # or copy .crush locally if it sits on NFS
Defensive patterns

Strategy: retry

Validate before calling

# Pre-check DB accessibility before running stats
db="${CRUSH_DATA_DIR:-.crush}/crush.db"
[ -f "$db" ] || { echo "DB missing: $db" >&2; exit 1; }
[ -r "$db" ] || { echo "DB unreadable: $db" >&2; exit 1; }
sqlite3 "$db" 'PRAGMA integrity_check;' | grep -q '^ok' || { echo "DB corrupt" >&2; exit 1; }

Try / catch

if err := gatherStats(ctx, dbPath); err != nil {
    var lockedErr sqlite3.Error
    if errors.As(err, &lockedErr) && errors.Is(err, sqlite3.ErrLocked) || strings.Contains(err.Error(), "locked") {
        time.Sleep(500 * time.Millisecond)
        return gatherStats(ctx, dbPath) // retry after writer releases lock
    }
    return err
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/fa067b98ff07f9f0. Report an issue: GitHub.