{"record":{"id":"9dd4c8162a00ac8f","repo":"gitbutlerapp/gitbutler","slug":"no-comment-with-id-id","errorCode":null,"errorMessage":"No comment with id {id}","messagePattern":"No comment with id (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-comments/src/lib.rs","lineNumber":347,"sourceCode":"pub struct Listing {\n    /// The re-anchored, unarchived comments.\n    pub comments: Vec<DiffComment>,\n    /// Whether the listing wrote to the store (persisted drift, auto-archived comments, or\n    /// purged old archived rows). Callers that bridge processes can use this to notify other\n    /// consumers of the store.\n    pub persisted_changes: bool,\n}\n\n/// Replace the payload of the unarchived comment with the given `id`.\npub fn update_payload(\n    store: &CommentStore,\n    id: &str,\n    payload: String,\n    now_ms: i64,\n) -> anyhow::Result<()> {\n    store.update(|comments| {\n        let Some(comment) = comments.iter_mut().find(|c| c.id == id) else {\n            bail!(\"No comment with id {id}\");\n        };\n        if comment.archived_at_ms.is_some() {\n            bail!(\"Comment {id} is archived and cannot be updated\");\n        }\n        comment.payload = payload;\n        comment.updated_at_ms = now_ms;\n        Ok(())\n    })\n}\n\n/// Archive the comment with the given `id`, hiding it from all future listings.\n/// Returns `false` if the comment does not exist or was already archived.\npub fn archive_comment(store: &CommentStore, id: &str, now_ms: i64) -> anyhow::Result<bool> {\n    store.update(|comments| {\n        Ok(\n            match comments\n                .iter_mut()\n                .find(|c| c.id == id && c.archived_at_ms.is_none())","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-comments/src/lib.rs#L329-L365","documentation":"update_payload() searches the comment store for the given id inside store.update() and bails when no comment matches. Ids are caller-supplied strings, so this is a plain not-found against persisted CommentStore state (including archived comments — they are found but rejected separately).","triggerScenarios":"Calling update_payload with an id from a stale listing, a typo'd/truncated id, or against a different CommentStore file than the one the comment was created in.","commonSituations":"UI editing a comment deleted in another session/window; id round-tripped through serialization losing characters; store path changed between create and update (different repo/workspace directory).","solutions":["Re-list comments from the same store and use the current id","Verify the store path is identical to the one used at creation (same repo/workspace)","Treat not-found as benign in edit flows: refresh the view and let the user re-create the comment","Sanitize ids end-to-end so they survive serialization unchanged"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify the id against the same store before updating\nlet exists = store.with(|comments| comments.iter().any(|c| c.id == id))?;\nanyhow::ensure!(exists, \"comment {id} not in this store — refresh the listing\");","typeGuard":null,"tryCatchPattern":"match but_comments::update_payload(&store, id, payload, now) {\n    Err(e) if e.to_string().contains(\"No comment with id\") =>\n        refresh_and_recreate(&store, payload).await, // self-heal stale edits\n    r => r?,\n}","preventionTips":["Pass ids straight from a fresh listing, never from cached UI state","Keep create/update flows pointed at the same CommentStore path","Treat not-found edits as a soft conflict in multi-session UIs"],"tags":["comments","store","not-found","id"],"backgroundTag":"record-not-found","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}