{"record":{"id":"6087eb6e50d7e253","repo":"atuinsh/atuin","slug":"failed-to-parse-uuid-for-local-store-status","errorCode":null,"errorMessage":"failed to parse uuid for local store status","messagePattern":"failed to parse uuid for local store status","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-client/src/record/sqlite_store.rs","lineNumber":305,"sourceCode":"        }\n    }\n\n    pub async fn status(&self) -> Result<RecordStatus> {\n        let mut status = RecordStatus::new();\n\n        let res: Result<Vec<(String, String, i64)>, sqlx::Error> =\n            sqlx::query_as(\"select host, tag, max(idx) from store group by host, tag\")\n                .fetch_all(&self.pool)\n                .await;\n\n        let res = match res {\n            Err(e) => return Err(eyre!(\"failed to fetch local store status: {}\", e)),\n            Ok(v) => v,\n        };\n\n        for i in res {\n            let host = HostId(\n                Uuid::from_str(i.0.as_str()).expect(\"failed to parse uuid for local store status\"),\n            );\n\n            status.set_raw(host, RecordTag::from(i.1), i.2 as u64);\n        }\n\n        Ok(status)\n    }\n\n    pub async fn all_tagged(\n        &self,\n        tag: &RecordTag,\n    ) -> Result<Vec<Record<paseto_v4::EncryptedData>>> {\n        let res = sqlx::query(\"select * from store where tag = ?1 order by timestamp asc\")\n            .bind(tag.as_str())\n            .map(Self::query_row)\n            .fetch_all(&self.pool)\n            .await?;\n","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/atuinsh/atuin/blob/15fe1318f1df51de604262eb50734c9883d48e7b/crates/atuin-client/src/record/sqlite_store.rs#L287-L323","documentation":"Panic while building the local record-store status: the query `select host, tag, max(idx) from store group by host, tag` succeeds, then each host string is parsed back into a Uuid. Because every store row's host is a UUID at write time, a parse failure means at least one host group in the table holds non-UUID text — store corruption. Unlike query_row's fail-fast comment, this site is inside a function returning Result, yet still uses expect.","triggerScenarios":"Calling the local store status builder (used by `atuin sync` status calculation and store status commands) when the store table contains any host value that is not a UUID. One corrupt row anywhere in the table poisons the whole status computation because the GROUP BY aggregates all hosts.","commonSituations":"Corrupt or externally modified store.db; a torn write after power loss; a fork or script having inserted rows with arbitrary host strings; restored-from-partial-backup data directories.","solutions":["List the offending host values: `sqlite3 ~/.local/share/atuin/store.db 'select host, count(*) from store group by host;'` and look for non-UUID strings","Restore the store from backup, or delete just the bad rows (`delete from store where host = '<bad value>';`) if the rest is intact, then re-run sync","If the store is broadly damaged, move it aside and rebuild the local store via `atuin sync` from the server","When patching the crate, replace the expect with `let host = Uuid::from_str(&i.0).wrap_err(\"invalid host in local store status\")?;` since the enclosing function already returns Result"],"exampleFix":"// before (crates/atuin-client/src/record/sqlite_store.rs:305)\nlet host = HostId(Uuid::from_str(i.0.as_str()).expect(\"failed to parse uuid for local store status\"));\n// after\nlet host = HostId(Uuid::from_str(i.0.as_str()).wrap_err(\"failed to parse uuid for local store status\")?);","handlingStrategy":"validation","validationCode":"async fn status_hosts_valid(pool: &sqlx::SqlitePool) -> Result<bool, sqlx::Error> {\n    let rows: Vec<(String, i64)> = sqlx::query_as(\n        \"select host, count(*) from store group by host\",\n    )\n    .fetch_all(pool)\n    .await?;\n    Ok(rows.iter().all(|(host, _)| uuid::Uuid::parse_str(host).is_ok()))\n}","typeGuard":"fn is_valid_uuid(s: &str) -> bool {\n    uuid::Uuid::parse_str(s).is_ok()\n}","tryCatchPattern":"let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    tokio::task::block_on(store.status())\n}));\nmatch res {\n    Ok(Ok(status)) => { /* compute sync tail */ }\n    Ok(Err(e)) => return Err(e.into()),\n    Err(_) => tracing::error!(\"status build hit a bad host UUID — prune bad rows and re-sync\"),\n}","preventionTips":["Pre-check grouped host values before running sync status on a store you did not write","Delete bad rows selectively rather than wiping the whole store","Keep server-side records as the source of truth so a local store can always be rebuilt"],"tags":["rust","sqlite","uuid","panic","data-corruption","record-store","sync-status"],"backgroundTag":"invalid-uuid-string","analyzedSha":"15fe1318f1df51de604262eb50734c9883d48e7b","analyzedAt":"2026-08-19T08:56:57.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}