{"record":{"id":"95af555f3456da29","repo":"Hmbown/CodeWhale","slug":"agent-mail-message-id-already-exists-with-dif","errorCode":null,"errorMessage":"Agent Mail message id '{}' already exists with different delivery intent","messagePattern":"Agent Mail message id '(.+?)' already exists with different delivery intent","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":3637,"sourceCode":"        {\n            bail!(\n                \"Agent Mail ownership denied: source and destination must belong to the same runtime owner and workspace\"\n            );\n        }\n        let expected_sender = agent_mail_sender_identity(&source_thread)?;\n        if request.sender.identity != expected_sender {\n            bail!(\n                \"Agent Mail ownership denied: sender identity does not own the source task/session\"\n            );\n        }\n\n        let (envelope, idempotent_replay) = {\n            let _mail_mutation = self.store.mail_mutation.lock();\n            let path = self.store.mail_path(&request.message_id)?;\n            if path.exists() {\n                let persisted = self.store.load_agent_mail(&request.message_id)?;\n                if !persisted.matches_send_request(&request) {\n                    bail!(\n                        \"Agent Mail message id '{}' already exists with different delivery intent\",\n                        request.message_id\n                    );\n                }\n                (persisted, true)\n            } else {\n                let envelope = AgentMailEnvelope {\n                    schema_version: AGENT_MAIL_SCHEMA_VERSION,\n                    message_id: request.message_id,\n                    source,\n                    destination,\n                    sender: request.sender,\n                    summary: request.summary,\n                    evidence: request.evidence,\n                    delivery_mode: request.delivery_mode,\n                    trigger_turn: request.trigger_turn,\n                    hop_count: request.hop_count,\n                    status: AgentMailStatus::Queued,","sourceCodeStart":3619,"sourceCodeEnd":3655,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/runtime_threads.rs#L3619-L3655","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet message_id = AgentMailMessageId::new(format!(\"mail-{thread_id}\"))?; // stable id, changing body\n\n// after\nlet message_id = AgentMailMessageId::new(uuid::Uuid::new_v4().to_string())?; // new id per intent","handlingStrategy":"validation","validationCode":"// Replay the exact original request on retry; new intent gets a new id.\nif retrying_same_send {\n    return original_request.clone(); // byte-identical -> idempotent replay\n} else {\n    request.message_id = AgentMailMessageId::new(uuid::Uuid::new_v4().to_string())?;\n}","typeGuard":null,"tryCatchPattern":"// Conflict: load the persisted envelope and decide.\nmatch manager.queue_agent_mail(request).await {\n    Ok(resp) => Ok(resp),\n    Err(e) if e.to_string().contains(\"different delivery intent\") => {\n        let persisted = manager.load_agent_mail(&request.message_id).await?; // advisory\n        Err(anyhow::anyhow!(\"message_id {} already used with different intent; generate a new id\", request.message_id))\n    }\n    Err(e) => Err(e),\n}","preventionTips":["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"],"tags":["agent-mail","idempotency","conflict","duplicate-id"],"backgroundTag":"idempotency-key-conflict","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}