{"record":{"id":"9b4540487fa76edc","repo":"atuinsh/atuin","slug":"issue-in-stats-exits-query","errorCode":null,"errorMessage":"issue in stats exits query","messagePattern":"issue in stats exits query","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-client/src/database.rs","lineNumber":955,"sourceCode":"        // 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)>,\n        ) = tokio::try_join!(\n            sqlx::query(sqlx::AssertSqlSafe(prev))\n                .bind(h.timestamp.unix_timestamp_nanos() as i64)\n                .bind(&h.session)","sourceCodeStart":937,"sourceCodeEnd":973,"githubUrl":"https://github.com/atuinsh/atuin/blob/202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1/crates/atuin-client/src/database.rs#L937-L973","documentation":"The fifth stats expect() panic (database.rs:955), rendering the 'exits' builder for `atuin stats`. Same family and same semantics: the query shape is hard-coded (exit-code aggregation over history), sql() failure is impossible from user input, and the expect exists to convert 'builder misused' into a loud, reportable crash. All seven rendered strings are then executed concurrently with tokio::try_join!.","triggerScenarios":"Running `atuin stats` against a build with a regression in the exits builder construction.","commonSituations":"Development-only occurrences; users should treat any sighting as an upstream bug and update/report.","solutions":["Update Atuin and report the panic with the backtrace","Rule out a stale custom build by rebuilding from a clean tree","Developers: keep stats builders covered by rendering unit tests","Run `atuin stats` as a smoke test after touching the stats code"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"let handle = std::thread::spawn(move || db.stats());\nif handle.join().is_err() { /* exits-query builder panic: report upstream */ }","preventionTips":["Unit-test the exits builder rendering when modifying stats code","Run `atuin stats` as a smoke test after builds","Report sightings upstream with version and backtrace","Avoid hand-editing the stats SQL construction without test coverage"],"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"}