zed-industries/zed · error

Message not found

Error message

Message not found

What it means

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.

Source

Thrown at crates/agent/src/thread.rs:2371

            },
        );
        cx.emit(TokenUsageUpdated(self.latest_token_usage()));
        cx.notify();
    }

    pub fn truncate(
        &mut self,
        client_user_message_id: ClientUserMessageId,
        cx: &mut Context<Self>,
    ) -> Result<()> {
        self.cancel(cx).detach();
        // Clear pending message since cancel will try to flush it asynchronously,
        // and we don't want that content to be added after we truncate
        self.pending_message.take();
        let Some(position) = self.messages.iter().position(|msg| {
            matches!(&**msg, Message::User(UserMessage { id, .. }) if id == &client_user_message_id)
        }) else {
            return Err(anyhow!("Message not found"));
        };

        for message in self.messages.drain(position..) {
            match &*message {
                Message::User(message) => {
                    self.request_token_usage.remove(&message.id);
                }
                Message::Agent(_) | Message::Resume | Message::Compaction(_) => {}
            }
        }
        self.clear_summary();
        cx.notify();
        Ok(())
    }

    pub fn latest_request_token_usage(&self) -> Option<language_model::TokenUsage> {
        let last_user_message = self.last_user_message()?;
        let tokens = self.request_token_usage.get(&last_user_message.id)?;

View on GitHub (pinned to bc538def45)

Solutions

  1. Re-read the thread's current messages and call truncate with an id from that fresh snapshot
  2. Before calling truncate, verify the id still exists among the thread's user messages (see validation snippet)
  3. Treat the error as 'history changed': refresh the message list instead of retrying the same id
  4. Verify the id was issued for this thread and not for a parent or subagent thread

Example fix

// before
thread.update(cx, |thread, cx| thread.truncate(message_id, cx))?;

// after
let still_present = thread.read(cx).messages().iter().any(|msg| {
    matches!(&**msg, Message::User(UserMessage { id, .. }) if id == &message_id)
});
if still_present {
    thread.update(cx, |thread, cx| thread.truncate(message_id, cx))?;
} else {
    reload_message_list(cx);
}
Defensive patterns

Strategy: validation

Validate before calling

// Run before truncate: confirm the anchor message is still in the thread.
fn can_truncate(thread: &Entity<Thread>, id: ClientUserMessageId, cx: &App) -> bool {
    thread.read(cx).messages().iter().any(|msg| {
        matches!(
            &**msg,
            Message::User(UserMessage { id: msg_id, .. }) if msg_id == &id
        )
    })
}

Type guard

fn user_message_position(
    messages: &[Arc<Message>],
    id: ClientUserMessageId,
) -> Option<usize> {
    messages.iter().position(|msg| {
        matches!(&**msg, Message::User(UserMessage { id: msg_id, .. }) if msg_id == &id)
    })
}

Try / catch

match thread.truncate(message_id, cx) {
    Ok(()) => {}
    Err(err) if err.to_string() == "Message not found" => {
        // Anchor message is gone; resynchronize instead of retrying the same id.
        reload_message_list();
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/8fc34c61a74e36f6. Report an issue: GitHub.