Hmbown/CodeWhale · error · anyhow::Error
Agent Mail ownership denied: message does not belong to this
Error message
Agent Mail ownership denied: message does not belong to this destination
What it means
mark_agent_mail_read resolved the calling thread's address and the loaded envelope's destination does not equal it. Only the destination thread may transition an envelope to Read; the source (or any third thread) cannot mark mail read on someone else's behalf.
Source
Thrown at crates/tui/src/runtime_threads.rs:3706
.collect::<Vec<_>>();
Ok(inbox)
})
.await
.context("Agent Mail inbox task panicked")?
}
pub async fn mark_agent_mail_read(
&self,
thread_id: &str,
message_id: &AgentMailMessageId,
) -> Result<AgentMailEnvelope> {
let thread = self.get_thread(thread_id).await?;
let address = agent_mail_address(&self.store.owner_id, &thread)?;
let envelope = {
let _mail_mutation = self.store.mail_mutation.lock();
let mut envelope = self.store.load_agent_mail(message_id)?;
if envelope.destination != address {
bail!("Agent Mail ownership denied: message does not belong to this destination");
}
match envelope.status {
AgentMailStatus::Read => envelope,
AgentMailStatus::Delivered => {
envelope.status = AgentMailStatus::Read;
envelope.read_at = Some(Utc::now());
self.store.save_agent_mail(&envelope)?;
envelope
}
_ => bail!("Agent Mail can be marked read only after delivery"),
}
};
self.emit_agent_mail_event(AGENT_MAIL_EVENT_READ, &envelope)
.await?;
Ok(envelope)
}
/// Claim and project one envelope into the existing destination turnView on GitHub (pinned to 0c42157ee5)
Solutions
- Fetch the destination thread's mail list first and only mark-read ids that appear in it
- Ensure the client passes the recipient thread's id, not the sender's or a global one
- Validate envelope.destination against the calling thread before invoking if the id came from untrusted input
- Check for copy-paste of message_id from a different envelope
Defensive patterns
Strategy: validation
Validate before calling
// Only mark read mail that is addressed to the calling thread.
let envelope = manager.load_agent_mail(message_id).await?;
if !envelope.destination_belongs_to(thread_id) { // compare address parts as the runtime does
return Err(anyhow::anyhow!("message not addressed to this thread"));
}
manager.mark_agent_mail_read(thread_id, message_id).await?; Prevention
- Drive read-marking from the destination thread's own mail list, not an owner-wide query
- Pass the recipient thread's id; never a sender or global context
- Copy message ids exactly - truncation resolves to a foreign envelope
When it happens
Trigger: Calling mark_agent_mail_read(thread_id, message_id) where message_id was sent to a different thread - wrong message id from a list, or a client passing the source/author thread instead of the recipient. Check at runtime_threads.rs:3704-3707.
Common situations: UI lists all mail for an owner and passes the wrong thread context; a script iterates mailboxes and reuses one thread_id for all messages; message id typo or truncation.
Related errors
- Agent Mail ownership denied: source and destination must bel
- Agent Mail ownership denied: sender identity does not own th
- Agent Mail can be marked read only after delivery
- external credential access is disabled for {}
- tool '{tool_name}' on MCP server '{server_name}' is blocked
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/758928e5ecd9e02d.
Report an issue: GitHub.