atuinsh/atuin · error

Failed to compute stats

Error message

Failed to compute stats

What it means

A panic from `.expect()` on the `Option` returned by `atuin_history::stats::compute` in `atuin wrapped` (wrapped.rs:348). `compute` does not return a `Result` — it returns `None` when, after filtering, `top.is_empty()` (stats.rs:286-288): every command in the supplied history was skipped by `stats.ignored_commands`, collapsed away by `common_prefix`/`interesting_command`, or reduced to an empty string so the `.windows(ngram_size)` n-gram count produced nothing. The empty-history case is already guarded earlier (wrapped.rs:320), so this panic fires only when the year has history entries but zero usable stats.

Source

Thrown at crates/atuin/src/command/client/wrapped.rs:348

            let host_id = Settings::host_id().await?;
            let alias_store = AliasStore::new(store, host_id, encryption_key);

            alias_store
                .aliases()
                .await
                .unwrap_or_default()
                .into_iter()
                .map(|a| (a.name, a.value))
                .collect()
        } else {
            HashMap::new()
        }
    } else {
        HashMap::new()
    };

    // Compute overall stats using existing functionality
    let stats = compute(settings, &history, 10, 1).expect("Failed to compute stats");
    let wrapped_stats = WrappedStats::new(settings, &stats, &history, &alias_map);

    // Print wrapped format
    print_wrapped_header(year);

    println!("🎉 In {year}, you typed {} commands!", stats.total_commands);
    println!(
        "   That's ~{} commands every day\n",
        stats.total_commands / 365
    );

    println!("Your Top Commands:");
    atuin_history::stats::pretty_print(stats.clone(), 1, theme);
    println!();

    print_fun_facts(&wrapped_stats, &stats, year);

    Ok(())

View on GitHub (pinned to 202f6ad98e)

Solutions

  1. Handle the `None` case gracefully instead of `.expect`: use `let Some(stats) = ... else { print a friendly 'no usable commands' message; return Ok(()) }`
  2. Review `stats.ignored_commands` and `common_prefix` in `~/.config/atuin/config.toml` and narrow the patterns
  3. Inspect the year's history for empty or env-only commands: `atuin search --before <end> --after <start> <cmd>` or query the sqlite DB, and clean/purge junk entries
  4. As a library fix, have `wrapped` skip the pretty-print block (or lower the filter) when `compute` returns `None`

Example fix

// before
let stats = compute(settings, &history, 10, 1).expect("Failed to compute stats");

// after
let Some(stats) = compute(settings, &history, 10, 1) else {
    println!("No usable commands found for {year} — check 'stats.ignored_commands' and 'common_prefix' in your config");
    return Ok(());
};
Defensive patterns

Strategy: fallback

Validate before calling

// Before computing, check the year yields at least one usable command
let usable = history.iter().any(|h| {
    let c = atuin_history::stats::strip_leading_env_vars(h.command.trim());
    !c.is_empty()
        && !settings.stats.ignored_commands.iter()
            .any(|ig| ig == &atuin_history::stats::interesting_command(settings, c))
});
if !usable {
    println!("No usable commands for {year} — check your stats ignore rules");
    return Ok(());
}

Type guard

// compute() itself is the guard — treat it as a checked operation:
let stats = match compute(settings, &history, 10, 1) {
    Some(s) => s,
    None => return Ok(()), // nothing reportable for this year
};

Try / catch

// Rust Option, not exception — use let-else so absence is a normal outcome:
let Some(stats) = compute(settings, &history, 10, 1) else {
    println!("No usable commands found for {year}");
    return Ok(());
};

Prevention

When it happens

Trigger: Running `atuin wrapped <year>` (or the month-appropriate default year) where `db.range(start, end)` returns non-empty history but `compute(settings, &history, 10, 1)` yields `None`: e.g. every command matches an `ignored_commands`/`common_prefix` entry in config.toml, or entries whose command strings are empty (or only env-var prefixes) after `strip_leading_env_vars` and trimming, leaving no n-grams for `windows(1)`.

Common situations: An over-broad `stats.ignored_commands` or `common_prefix: ["*"]`-style config that filters the entire year; imported shell history containing blank/env-assignment-only entries; a year where the user only ran commands that Atuin's default prefix rules collapse to nothing.

Related errors


AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16). Data as JSON: /api/errors/e4860e611b7bd3f5. Report an issue: GitHub.