{"record":{"id":"08ffc1c29d01e945","repo":"Hmbown/CodeWhale","slug":"agent-mail-ownership-denied-sender-identity-does","errorCode":null,"errorMessage":"Agent Mail ownership denied: sender identity does not own the source task/session","messagePattern":"Agent Mail ownership denied: sender identity does not own the source task/session","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":3626,"sourceCode":"        request.validate().map_err(|error| anyhow!(error))?;\n        if request.source_thread_id == request.destination_thread_id {\n            bail!(\"Agent Mail source and destination threads must differ\");\n        }\n\n        let source_thread = self.get_thread(&request.source_thread_id).await?;\n        let destination_thread = self.get_thread(&request.destination_thread_id).await?;\n        let source = agent_mail_address(&self.store.owner_id, &source_thread)?;\n        let destination = agent_mail_address(&self.store.owner_id, &destination_thread)?;\n        if source.owner_id != destination.owner_id\n            || source.workspace_id != destination.workspace_id\n        {\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 {","sourceCodeStart":3608,"sourceCodeEnd":3644,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/runtime_threads.rs#L3608-L3644","documentation":"queue_agent_mail derived the expected sender identity from the source thread (agent_mail_sender_identity) and it does not match request.sender.identity. Only the identity that owns the source task/session may send mail in its name; a mismatch means the request is spoofing (or misconfiguring) the sender.","triggerScenarios":"Passing a hand-crafted sender.identity that differs from the identity the source thread was created with - e.g. reusing a sender block from another thread, or an agent forwarding mail without rewriting sender to its own identity. Check at runtime_threads.rs:3625-3629.","commonSituations":"Copy-pasted request templates with a stale identity; hop-forwarding code that preserves the original sender instead of re-signing as the forwarding thread; identity format drift after a protocol version change.","solutions":["Derive sender.identity from the source thread's own record (the same way agent_mail_sender_identity does) instead of accepting it from the client","When forwarding, set sender to the forwarding thread's identity and increment hop_count","Update stale templates/fixtures after any identity-format change","Treat this error as a possible spoofing attempt in server-side code: log and reject, do not auto-correct"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Derive the sender identity from the source thread instead of trusting client input.\nrequest.sender.identity = sender_identity_of(&manager.get_thread(&request.source_thread_id).await?);\nmanager.queue_agent_mail(request).await?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-craft sender.identity; derive it from the source thread record","When forwarding mail, re-sign as the forwarding thread and bump hop_count","After identity-format changes, refresh stored templates and fixtures"],"tags":["agent-mail","authorization","sender-identity","spoofing"],"backgroundTag":"authorization-denied","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}