{"record":{"id":"111f74140ff105eb","repo":"atuinsh/atuin","slug":"issue-in-stats-day-of-week-query","errorCode":null,"errorMessage":"issue in stats day of week query","messagePattern":"issue in stats day of week query","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-client/src/database.rs","lineNumber":890,"sourceCode":"        // _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 =\n            duration_over_time.sql().expect(\"issue in stats duration over time query\");\n\n        // The queries are all independent, so run them concurrently on the pool.\n        #[allow(clippy::type_complexity)]\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::row_to_history)","sourceCodeStart":872,"sourceCodeEnd":908,"githubUrl":"https://github.com/atuinsh/atuin/blob/15fe1318f1df51de604262eb50734c9883d48e7b/crates/atuin-client/src/database.rs#L872-L908","documentation":"Panic while compiling the day-of-week histogram query. The guarded builder (database.rs:862-869) emits `strftime('%w', ROUND(timestamp / 1000000000), 'unixepoch') AS day_of_week` plus a count, with `command = ?1` and group_by on the alias — fully static, so sql() Err is an unreachable invariant.","triggerScenarios":"Database::stats() after swapping sqlbuilder to a version that rejects something in this chain (commonly group_by on an aliased field or the complex strftime expression in fields()). No database content or user input affects the builder.","commonSituations":"sqlbuilder major-version bumps during dependency maintenance; a vendored patch changing how group_by/aliases are validated. Not seen on the committed lockfile.","solutions":["Revert the sqlbuilder change in Cargo.lock to the last known-good version","Add the day_of_week builder to the CI unit test that asserts all stats queries compile to SQL","If genuinely failing on the pinned version, report upstream with the exact strftime/group_by combination, since that is the most exotic expression of the seven stats queries"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let stats = std::panic::catch_unwind(|| {\n    tokio::task::block_on(database.stats(&history))\n});\nlet _ = stats.map_err(|_| tracing::error!(\"stats day-of-week builder panicked\"));","preventionTips":["Version-pin sqlbuilder and audit lockfile changes","Cover the strftime/group_by builder in upgrade-time unit tests","Escalate hits as a dependency bug, not an operational fault"],"tags":["rust","sqlite","sqlbuilder","panic","invariant","stats","strftime","unreachable"],"backgroundTag":"query-builder-invalid-state","analyzedSha":"15fe1318f1df51de604262eb50734c9883d48e7b","analyzedAt":"2026-08-19T08:56:57.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}