charmbracelet/crush · error

get average response time: %w

Error message

get average response time: %w

What it means

gatherStats fetches the average assistant response time via queries.GetAverageResponseTime and wraps any SQL failure with this message. The query computes a millisecond average from message timestamps; failures are database-level (lock, access, corruption, schema), not data-quality issues.

Source

Thrown at internal/cmd/stats.go:635

	// Recent activity (last 30 days).
	recent, err := queries.GetRecentActivity(ctx)
	if err != nil {
		return nil, fmt.Errorf("get recent activity: %w", err)
	}
	for _, r := range recent {
		stats.RecentActivity = append(stats.RecentActivity, DailyActivity{
			Day:          fmt.Sprintf("%v", r.Day),
			SessionCount: r.SessionCount,
			TotalTokens:  nullFloat64ToInt64(r.TotalTokens),
			Cost:         r.Cost.Float64,
		})
	}

	// Average response time.
	avgResp, err := queries.GetAverageResponseTime(ctx)
	if err != nil {
		return nil, fmt.Errorf("get average response time: %w", err)
	}
	stats.AvgResponseTimeMs = toFloat64(avgResp) * 1000

	// Tool usage.
	toolUsage, err := queries.GetToolUsage(ctx)
	if err != nil {
		return nil, fmt.Errorf("get tool usage: %w", err)
	}
	for _, t := range toolUsage {
		if name, ok := t.ToolName.(string); ok && name != "" {
			stats.ToolUsage = append(stats.ToolUsage, ToolUsage{
				ToolName:  name,
				CallCount: t.CallCount,
			})
		}
	}

	// Hour/day heatmap.

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Ensure no other crush instance is writing, then rerun
  2. Verify DB readability and integrity; restore or regenerate a corrupt DB
  3. Match the crush binary version to the DB schema (reinstall/upgrade)
  4. Move .crush to a local filesystem if it currently lives on NFS/network storage

Example fix

// before
crush stats  # get average response time: disk I/O error
// after
sqlite3 .crush/crush.db 'PRAGMA integrity_check;'  # if corrupt: restore or delete crush.db, then rerun
Defensive patterns

Strategy: retry

Validate before calling

db="${CRUSH_DATA_DIR:-.crush}/crush.db"
if ! sqlite3 "$db" 'SELECT count(*) FROM messages;' >/dev/null 2>&1; then
  echo "Sessions DB not queryable (locked/missing/corrupt): $db" >&2
  exit 1
fi

Try / catch

if err := gatherStats(ctx, dbPath); err != nil {
    if strings.Contains(err.Error(), "get average response time") && strings.Contains(err.Error(), "locked") {
        time.Sleep(1 * time.Second)
        return gatherStats(ctx, dbPath)
    }
    return err
}

Prevention

When it happens

Trigger: GetAverageResponseTime fails: SQLite DB locked or unreadable, corrupt database file, or the prepared statement failing due to missing/mismatched schema in the sessions DB.

Common situations: Concurrent crush sessions contending for the write lock; crash-interrupted writes corrupting crush.db; schema/version mismatch; DB on network storage with unreliable locking.

Related errors


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