{"record":{"id":"7eae4d6354bbfe31","repo":"zed-industries/zed","slug":"message-not-found","errorCode":null,"errorMessage":"message not found","messagePattern":"message not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/acp_thread/src/acp_thread.rs","lineNumber":3989,"sourceCode":"                continue;\n            };\n            if self\n                .elicitations\n                .cancel_elicitation_by_id(elicitation_id, true)\n            {\n                cx.emit(AcpThreadEvent::EntryUpdated(ix));\n            }\n        }\n    }\n\n    /// Restores the git working tree to the state at the given checkpoint (if one exists)\n    pub fn restore_checkpoint(\n        &mut self,\n        client_id: ClientUserMessageId,\n        cx: &mut Context<Self>,\n    ) -> Task<Result<()>> {\n        let Some((_, message)) = self.user_message_mut(&client_id) else {\n            return Task::ready(Err(anyhow!(\"message not found\")));\n        };\n\n        let checkpoint = message\n            .checkpoint\n            .as_ref()\n            .map(|c| c.git_checkpoint.clone());\n\n        // Cancel any in-progress generation before restoring\n        let cancel_task = self.cancel(cx);\n        let rewind = self.rewind(client_id.clone(), cx);\n        let git_store = self.project.read(cx).git_store().clone();\n\n        cx.spawn(async move |_, cx| {\n            cancel_task.await;\n            rewind.await?;\n            if let Some(checkpoint) = checkpoint {\n                git_store\n                    .update(cx, |git, cx| git.restore_checkpoint(checkpoint, cx))","sourceCodeStart":3971,"sourceCodeEnd":4007,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/acp_thread/src/acp_thread.rs#L3971-L4007","documentation":"restore_checkpoint() requires a ClientUserMessageId that resolves to a user message entry via user_message_mut(); when no entry with that id exists it returns a ready Err task 'message not found'. The id must belong to a live user message in this thread (that is where checkpoints are stored).","triggerScenarios":"Client sends restore_checkpoint with a message id that was rewound away (the entry was truncated out of the thread), an id from a different thread, or an id cached before the thread was cleared/restored from the database.","commonSituations":"Client UI keeps a stale checkpoint id across a rewind; double-click/racing restore where the first call rewinds entries and the second one misses; client reconnected after agent restart while reusing pre-restart message ids.","solutions":["Re-read the thread's entries and use a currently-present user message id before restoring","On receiving this error, refresh client-side thread state instead of retrying the same id","Debounce/disable the restore control while a restore is in flight"],"exampleFix":"// before\nlet task = thread.update(cx, |thread, cx| {\n    thread.restore_checkpoint(client_id.clone(), cx)\n});\n\n// after: verify the id is still a live user message first\nlet exists = thread.read(cx).entries.iter().any(|entry|\n    matches!(entry, ThreadEntry::User(m) if m.client_id == client_id));\nif !exists {\n    return Task::ready(Err(anyhow!(\"message not found\"))); // refresh UI state instead\n}\nlet task = thread.update(cx, |thread, cx| {\n    thread.restore_checkpoint(client_id.clone(), cx)\n});","handlingStrategy":"validation","validationCode":"// Verify the message id is a live user entry before restoring\nlet is_live = thread.read(cx).entries.iter().any(|entry| {\n    matches!(entry, ThreadEntry::User(m) if m.client_id == client_id)\n});\nif !is_live {\n    refresh_thread_state(); // re-fetch entries; do not call restore_checkpoint\n    return;\n}","typeGuard":null,"tryCatchPattern":"let task = thread.update(cx, |thread, cx| thread.restore_checkpoint(client_id.clone(), cx));\nif let Err(error) = task.await {\n    if error.to_string().contains(\"message not found\") {\n        // Stale id after a rewind — refresh entries and let the user re-pick.\n        refresh_thread_state();\n    } else {\n        return Err(error);\n    }\n}","preventionTips":["Derive checkpoint ids from freshly-read thread state, never from cached UI state","Debounce restore buttons so double activations cannot race a rewind","After any rewind/restore completes, invalidate cached message ids client-side"],"tags":["rust","zed","agent","checkpoint","acp","session"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}