{"record":{"id":"758928e5ecd9e02d","repo":"Hmbown/CodeWhale","slug":"agent-mail-ownership-denied-message-does-not-belo","errorCode":null,"errorMessage":"Agent Mail ownership denied: message does not belong to this destination","messagePattern":"Agent Mail ownership denied: message does not belong to this destination","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":3706,"sourceCode":"                .collect::<Vec<_>>();\n            Ok(inbox)\n        })\n        .await\n        .context(\"Agent Mail inbox task panicked\")?\n    }\n\n    pub async fn mark_agent_mail_read(\n        &self,\n        thread_id: &str,\n        message_id: &AgentMailMessageId,\n    ) -> Result<AgentMailEnvelope> {\n        let thread = self.get_thread(thread_id).await?;\n        let address = agent_mail_address(&self.store.owner_id, &thread)?;\n        let envelope = {\n            let _mail_mutation = self.store.mail_mutation.lock();\n            let mut envelope = self.store.load_agent_mail(message_id)?;\n            if envelope.destination != address {\n                bail!(\"Agent Mail ownership denied: message does not belong to this destination\");\n            }\n            match envelope.status {\n                AgentMailStatus::Read => envelope,\n                AgentMailStatus::Delivered => {\n                    envelope.status = AgentMailStatus::Read;\n                    envelope.read_at = Some(Utc::now());\n                    self.store.save_agent_mail(&envelope)?;\n                    envelope\n                }\n                _ => bail!(\"Agent Mail can be marked read only after delivery\"),\n            }\n        };\n        self.emit_agent_mail_event(AGENT_MAIL_EVENT_READ, &envelope)\n            .await?;\n        Ok(envelope)\n    }\n\n    /// Claim and project one envelope into the existing destination turn","sourceCodeStart":3688,"sourceCodeEnd":3724,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/runtime_threads.rs#L3688-L3724","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only mark read mail that is addressed to the calling thread.\nlet envelope = manager.load_agent_mail(message_id).await?;\nif !envelope.destination_belongs_to(thread_id) { // compare address parts as the runtime does\n    return Err(anyhow::anyhow!(\"message not addressed to this thread\"));\n}\nmanager.mark_agent_mail_read(thread_id, message_id).await?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["agent-mail","authorization","not-found","mailbox"],"backgroundTag":"authorization-denied","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}