{"record":{"id":"8fc34c61a74e36f6","repo":"zed-industries/zed","slug":"message-not-found-8fc34c","errorCode":null,"errorMessage":"Message not found","messagePattern":"Message not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/agent/src/thread.rs","lineNumber":2371,"sourceCode":"            },\n        );\n        cx.emit(TokenUsageUpdated(self.latest_token_usage()));\n        cx.notify();\n    }\n\n    pub fn truncate(\n        &mut self,\n        client_user_message_id: ClientUserMessageId,\n        cx: &mut Context<Self>,\n    ) -> Result<()> {\n        self.cancel(cx).detach();\n        // Clear pending message since cancel will try to flush it asynchronously,\n        // and we don't want that content to be added after we truncate\n        self.pending_message.take();\n        let Some(position) = self.messages.iter().position(|msg| {\n            matches!(&**msg, Message::User(UserMessage { id, .. }) if id == &client_user_message_id)\n        }) else {\n            return Err(anyhow!(\"Message not found\"));\n        };\n\n        for message in self.messages.drain(position..) {\n            match &*message {\n                Message::User(message) => {\n                    self.request_token_usage.remove(&message.id);\n                }\n                Message::Agent(_) | Message::Resume | Message::Compaction(_) => {}\n            }\n        }\n        self.clear_summary();\n        cx.notify();\n        Ok(())\n    }\n\n    pub fn latest_request_token_usage(&self) -> Option<language_model::TokenUsage> {\n        let last_user_message = self.last_user_message()?;\n        let tokens = self.request_token_usage.get(&last_user_message.id)?;","sourceCodeStart":2353,"sourceCodeEnd":2389,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/agent/src/thread.rs#L2353-L2389","documentation":"Returned by Thread::truncate when the supplied ClientUserMessageId does not match any Message::User entry in the thread's message list. Truncate uses that user message as the anchor and drains it plus every message after it, so it refuses to operate when the anchor is missing. The lookup only matches user messages; agent, resume, and compaction entries are skipped.","triggerScenarios":"Calling thread.truncate(client_user_message_id, cx) with an id that was never sent to this thread, was already removed by an earlier truncate, was dropped during context compaction, or was obtained from a different thread instance.","commonSituations":"An edit-message action firing twice, where the first truncate already drained the target; retrying with an id captured before a compaction reshaped the history; client-side state going stale after a cancel; passing an id loaded from another session or subagent thread.","solutions":["Re-read the thread's current messages and call truncate with an id from that fresh snapshot","Before calling truncate, verify the id still exists among the thread's user messages (see validation snippet)","Treat the error as 'history changed': refresh the message list instead of retrying the same id","Verify the id was issued for this thread and not for a parent or subagent thread"],"exampleFix":"// before\nthread.update(cx, |thread, cx| thread.truncate(message_id, cx))?;\n\n// after\nlet still_present = thread.read(cx).messages().iter().any(|msg| {\n    matches!(&**msg, Message::User(UserMessage { id, .. }) if id == &message_id)\n});\nif still_present {\n    thread.update(cx, |thread, cx| thread.truncate(message_id, cx))?;\n} else {\n    reload_message_list(cx);\n}","handlingStrategy":"validation","validationCode":"// Run before truncate: confirm the anchor message is still in the thread.\nfn can_truncate(thread: &Entity<Thread>, id: ClientUserMessageId, cx: &App) -> bool {\n    thread.read(cx).messages().iter().any(|msg| {\n        matches!(\n            &**msg,\n            Message::User(UserMessage { id: msg_id, .. }) if msg_id == &id\n        )\n    })\n}","typeGuard":"fn user_message_position(\n    messages: &[Arc<Message>],\n    id: ClientUserMessageId,\n) -> Option<usize> {\n    messages.iter().position(|msg| {\n        matches!(&**msg, Message::User(UserMessage { id: msg_id, .. }) if msg_id == &id)\n    })\n}","tryCatchPattern":"match thread.truncate(message_id, cx) {\n    Ok(()) => {}\n    Err(err) if err.to_string() == \"Message not found\" => {\n        // Anchor message is gone; resynchronize instead of retrying the same id.\n        reload_message_list();\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Source the id from the same message snapshot the UI currently renders","Never reuse an id captured before a compaction, cancel, or earlier truncate","Treat this error as a resync signal, not a retryable transient failure","Guard against double invocations of edit/truncate actions in the UI"],"tags":["rust","agent","thread","truncate","message-id","stale-state"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}