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

  1. If this is a genuine retry of the same send, replay the exact original request bytes - it will return the existing record as idempotent
  2. If intent changed, generate a new message_id (UUID v4) for the new send
  3. Never derive message_id from mutable content-plus-counter schemes that reset across restarts
  4. 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

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


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/95af555f3456da29. Report an issue: GitHub.