{"record":{"id":"bc9b6d7fe0198ecf","repo":"atuinsh/atuin","slug":"issue-in-stats-duration-over-time-query","errorCode":null,"errorMessage":"issue in stats duration over time query","messagePattern":"issue in stats duration over time query","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-client/src/database.rs","lineNumber":959,"sourceCode":"        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)>,\n        ) = tokio::try_join!(\n            sqlx::query(sqlx::AssertSqlSafe(prev))\n                .bind(h.timestamp.unix_timestamp_nanos() as i64)\n                .bind(&h.session)\n                .map(Self::query_history)\n                .fetch_optional(&self.pool),\n            sqlx::query(sqlx::AssertSqlSafe(next))\n                .bind(h.timestamp.unix_timestamp_nanos() as i64)","sourceCodeStart":941,"sourceCodeEnd":977,"githubUrl":"https://github.com/atuinsh/atuin/blob/202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1/crates/atuin-client/src/database.rs#L941-L977","documentation":"The last stats expect() panic (database.rs:959), rendering duration_over_time — the per-month average-duration query that uses SQLite strftime/ROUND(timestamp / 1000000000) to bucket by month and a HAVING duration > 0. The comment above it explains the bucketing exists because SQLite lacks a datetime type and string-sorting months would be wrong. As with all seven, the expect is an internal regression guard on a fixed query shape.","triggerScenarios":"Running `atuin stats` on a build where the duration_over_time builder was broken; the strftime expression and group_by/having clauses are the likely regression points.","commonSituations":"Development regressions around the duration-over-time feature (e.g. changing the timestamp division or month format string); not reachable via data.","solutions":["Update Atuin and report the panic upstream","Developers: verify the strftime format string and group_by alias stay consistent when editing","Add a unit test asserting the rendered SQL contains the month_year grouping","Smoke-test `atuin stats` after any change in this block"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"let handle = std::thread::spawn(move || db.stats());\nif handle.join().is_err() { /* duration-over-time builder panic: report upstream */ }","preventionTips":["Keep the strftime month format and group_by alias consistent when editing the duration query","Unit-test that the rendered SQL contains month_year grouping","Smoke-test `atuin stats` after touching the stats block","Report upstream occurrences with a backtrace"],"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"}