{"record":{"id":"984f5fe21f441111","repo":"atuinsh/atuin","slug":"issue-in-stats-previous-query","errorCode":null,"errorMessage":"issue in stats previous query","messagePattern":"issue in stats previous query","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-client/src/database.rs","lineNumber":951,"sourceCode":"            ])\n            .and_where(\"command = ?1\")\n            .group_by(\"day_of_week\");\n\n        // Intentionally format the string with 01 hardcoded. We want the average runtime for the\n        // _entire month_, but will later parse it as a datetime for sorting\n        // Sqlite has no datetime so we cannot do it there, and otherwise sorting will just be a\n        // string sort, which won't be correct.\n        let mut duration_over_time = SqlBuilder::select_from(\"history\");\n        duration_over_time\n            .fields(&[\n                \"strftime('01-%m-%Y', ROUND(timestamp / 1000000000), 'unixepoch') AS month_year\",\n                \"avg(duration) as duration\",\n            ])\n            .and_where(\"command = ?1\")\n            .group_by(\"month_year\")\n            .having(\"duration > 0\");\n\n        let prev = prev.sql().expect(\"issue in stats previous query\");\n        let next = next.sql().expect(\"issue in stats next query\");\n        let total = total.sql().expect(\"issue in stats average query\");\n        let average = average.sql().expect(\"issue in stats previous query\");\n        let exits = exits.sql().expect(\"issue in stats exits query\");\n        let day_of_week = day_of_week.sql().expect(\"issue in stats day of week query\");\n        let duration_over_time = duration_over_time\n            .sql()\n            .expect(\"issue in stats duration over time query\");\n\n        // The queries are all independent, so run them concurrently on the pool.\n        let (prev, next, total, average, exits, day_of_week, duration_over_time): (\n            _,\n            _,\n            (i64,),\n            (f64,),\n            Vec<(i64, i64)>,\n            Vec<(String, i64)>,\n            Vec<(String, f64)>,","sourceCodeStart":933,"sourceCodeEnd":969,"githubUrl":"https://github.com/atuinsh/atuin/blob/202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1/crates/atuin-client/src/database.rs#L933-L969","documentation":"One of seven expect() panics in the stats code (database.rs:951) that render the 'prev' SqlBuilder for Atuin's `atuin stats` command. The builders for prev/next/total/average/exits/day_of_week/duration_over_time are all fixed-shape SELECTs over the history table; sql() failing means the builder was left in an invalid state by a code change. Because the query shape is hard-coded, no user input, database size, or settings value can cause this — it is purely an internal regression guard.","triggerScenarios":"Running `atuin stats` (which builds all seven builders and renders them back-to-back); the panic itself only triggers after someone edited one of the builder constructions into an unrenderable state.","commonSituations":"Seen only during Atuin development around the stats feature; users hitting it should suspect a corrupted custom build or a genuinely new upstream bug and report it.","solutions":["Update to the latest Atuin release — this is an internal assertion, not a data problem","Report upstream with the exact panic message and version","Developers: after editing any stats builder, run `atuin stats` locally as a smoke test","Cover each stats builder with a unit test asserting .sql().is_ok()"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"// expect() panic: isolate if needed\nlet handle = std::thread::spawn(move || db.stats());\nmatch handle.join() {\n    Ok(Ok(s)) => { /* stats */ }\n    Ok(Err(e)) => { /* db error */ }\n    Err(_) => { /* internal stats-query panic: report upstream */ }\n}","preventionTips":["Run `atuin stats` as a smoke test after any local build touching database.rs","Unit-test that every stats builder renders (.sql().is_ok())","Keep stats builder shapes fixed unless a migration demands change","Report sightings upstream — no user data can trigger this"],"tags":["database","panic","invariant","stats","sqlite","sql-builder","rust","atuin"],"backgroundTag":"internal-invariant-panic","analyzedSha":"202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1","analyzedAt":"2026-08-16T19:30:24.731Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}