charmbracelet/crush · error

get usage by hour: %w

Error message

get usage by hour: %w

What it means

gatherStats fetches hourly usage distribution via queries.GetUsageByHour and wraps any SQL failure with this message. Same root causes as the other gatherStats queries: the SQLite database must be open, unlocked, and schema-compatible for the query to succeed.

Source

Thrown at internal/cmd/stats.go:594

	}

	// Usage by model.
	modelUsage, err := queries.GetUsageByModel(ctx)
	if err != nil {
		return nil, fmt.Errorf("get usage by model: %w", err)
	}
	for _, m := range modelUsage {
		stats.UsageByModel = append(stats.UsageByModel, ModelUsage{
			Model:        m.Model,
			Provider:     m.Provider,
			MessageCount: m.MessageCount,
		})
	}

	// Usage by hour.
	hourlyUsage, err := queries.GetUsageByHour(ctx)
	if err != nil {
		return nil, fmt.Errorf("get usage by hour: %w", err)
	}
	for _, h := range hourlyUsage {
		stats.UsageByHour = append(stats.UsageByHour, HourlyUsage{
			Hour:         int(h.Hour),
			SessionCount: h.SessionCount,
		})
	}

	// Usage by day of week.
	dowUsage, err := queries.GetUsageByDayOfWeek(ctx)
	if err != nil {
		return nil, fmt.Errorf("get usage by day of week: %w", err)
	}
	for _, d := range dowUsage {
		stats.UsageByDayOfWeek = append(stats.UsageByDayOfWeek, DayOfWeekUsage{
			DayOfWeek:        int(d.DayOfWeek),
			DayName:          dayNames[int(d.DayOfWeek)],
			SessionCount:     d.SessionCount,

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Ensure no concurrent crush process holds the write lock, then rerun
  2. Confirm the DB exists and migrations are complete for your crush version
  3. If corrupted, remove or restore the DB and regenerate stats
  4. Use a local (non-NFS) data directory for reliable SQLite behavior

Example fix

// before
crush stats  # get usage by hour: database is locked (another session running)
// after
wait for/close the running crush session, then: crush stats
Defensive patterns

Strategy: retry

Validate before calling

db="${CRUSH_DATA_DIR:-.crush}/crush.db"
if fuser "$db" >/dev/null 2>&1; then echo "DB in use; wait and retry" >&2; exit 1; fi
sqlite3 "$db" 'PRAGMA integrity_check;' | grep -q ok || { echo "Corrupt DB" >&2; exit 1; }

Try / catch

if err := gatherStats(ctx, dbPath); err != nil {
    if strings.Contains(err.Error(), "get usage by hour") {
        select {
        case <-time.After(time.Second):
            return gatherStats(ctx, dbPath) // one retry for transient lock
        }
    }
    return err
}

Prevention

When it happens

Trigger: GetUsageByHour fails: DB file locked, missing, unreadable, corrupted, or the prepared statement fails due to schema drift between crush versions.

Common situations: DB held by a long-running crush session; .crush on a flaky network mount; interrupted upgrade leaving partial migration; --data-dir pointing at a directory without a valid crush.db.

Related errors


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