Hmbown/CodeWhale · error · anyhow::Error
Agent Mail message id '{}' already exists with different del
Error message
Agent Mail message id '{}' already exists with different delivery intent What it means
queue_agent_mail found an envelope already persisted at mail_path(message_id), but matches_send_request says it was stored with different delivery intent. message_id is an idempotency key: an exact replay returns the existing record; a same-id/different-content request is a conflict and fails closed to prevent silently rewriting history.
Source
Thrown at crates/tui/src/runtime_threads.rs:3637
{
bail!(
"Agent Mail ownership denied: source and destination must belong to the same runtime owner and workspace"
);
}
let expected_sender = agent_mail_sender_identity(&source_thread)?;
if request.sender.identity != expected_sender {
bail!(
"Agent Mail ownership denied: sender identity does not own the source task/session"
);
}
let (envelope, idempotent_replay) = {
let _mail_mutation = self.store.mail_mutation.lock();
let path = self.store.mail_path(&request.message_id)?;
if path.exists() {
let persisted = self.store.load_agent_mail(&request.message_id)?;
if !persisted.matches_send_request(&request) {
bail!(
"Agent Mail message id '{}' already exists with different delivery intent",
request.message_id
);
}
(persisted, true)
} else {
let envelope = AgentMailEnvelope {
schema_version: AGENT_MAIL_SCHEMA_VERSION,
message_id: request.message_id,
source,
destination,
sender: request.sender,
summary: request.summary,
evidence: request.evidence,
delivery_mode: request.delivery_mode,
trigger_turn: request.trigger_turn,
hop_count: request.hop_count,
status: AgentMailStatus::Queued,View on GitHub (pinned to 0c42157ee5)
Solutions
- If this is a genuine retry of the same send, replay the exact original request bytes - it will return the existing record as idempotent
- If intent changed, generate a new message_id (UUID v4) for the new send
- Never derive message_id from mutable content-plus-counter schemes that reset across restarts
- On conflict, load the persisted envelope first to decide whether the original send already suffices
Example fix
// before
let message_id = AgentMailMessageId::new(format!("mail-{thread_id}"))?; // stable id, changing body
// after
let message_id = AgentMailMessageId::new(uuid::Uuid::new_v4().to_string())?; // new id per intent Defensive patterns
Strategy: validation
Validate before calling
// Replay the exact original request on retry; new intent gets a new id.
if retrying_same_send {
return original_request.clone(); // byte-identical -> idempotent replay
} else {
request.message_id = AgentMailMessageId::new(uuid::Uuid::new_v4().to_string())?;
} Try / catch
// Conflict: load the persisted envelope and decide.
match manager.queue_agent_mail(request).await {
Ok(resp) => Ok(resp),
Err(e) if e.to_string().contains("different delivery intent") => {
let persisted = manager.load_agent_mail(&request.message_id).await?; // advisory
Err(anyhow::anyhow!("message_id {} already used with different intent; generate a new id", request.message_id))
}
Err(e) => Err(e),
} Prevention
- Treat message_id as write-once idempotency key: same id, same bytes forever
- Use UUID v4 per logical send; never derive ids from resettable counters
- Keep the original request body if you support client retries
When it happens
Trigger: Reusing a message_id (deterministic UUID, retry counter, or client cache) while any field of the request changed - summary, destination, sender, evidence, delivery mode. Check at runtime_threads.rs:3633-3640 under the mail_mutation lock.
Common situations: Client retry logic regenerates content but keeps the id; two agents derive the same deterministic id; a resend-after-edit flow that reuses the original id.
Related errors
- Dynamic tool call '{}' is already pending
- server '{server_name}' is not registered
- Trigger '{trigger_id}' cannot be canceled (status: {:?})
- duplicate agent profile id {}
- duplicate fleet task id {}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/95af555f3456da29.
Report an issue: GitHub.