A decision tree for the most common Exchange Online ticket. Work through it in order; each step narrows the problem and gives you the exact command or tool to use. Most missing-email cases resolve within the first four steps.
Missing email is rarely one problem. It is a family of problems, inbound and outbound, that share a symptom. The fastest route to the cause is to stop guessing and establish three facts first: is the message inbound or outbound, does a trace show it reaching Exchange Online at all, and what verdict did it get. Everything below is built around answering those in turn.
Vague tickets waste the most time. Pin down four things from the user before touching PowerShell: the exact sender and recipient addresses, the rough date and time, the subject line, and whether this is the first time or a pattern. With those, a message trace becomes precise instead of a fishing trip.
Is your user the sender or the recipient of the missing message?
They expected to receive it (inbound): the message originates outside and may never have reached your tenant. Go to Step 2.
They sent it and it did not arrive (outbound): the message left your tenant, or tried to. Skip to Step 6.
This single fork halves the search space. Inbound problems are usually filtering, routing or the message never arriving; outbound problems are usually rejection by the far end, which produces an NDR.
Run a trace for the message. Does it appear?
Get-MessageTraceV2 -RecipientAddress user@contoso.com -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) | Where-Object SenderAddress -like "*expectedsender.com" | Select-Object Received, SenderAddress, Subject, Status
No result at all: Exchange Online never saw it. The problem is upstream: the sender's own server, their SPF or your MX records, or the message was rejected at the edge before logging. Ask the sender to check their own outbound logs or any NDR they received. Confirm your domain MX points at Microsoft.
It appears with a Status: Exchange Online received it. The Status tells you what happened next, go to Step 3.
Use Get-MessageTraceV2, not the deprecated Get-MessageTrace. It reaches back 90 days and is the current cmdlet; see the migration guide for the behaviour differences. For commands you can fill in, the Message Trace category in the Exchange builder generates these.
Match the Status from the trace:
Delivered: Exchange Online put it in the mailbox. If the user cannot see it, the message is there but hidden, go to Step 4.
FilteredAsSpam / Quarantined: filtering caught it. Go to Step 5.
Failed: delivery was rejected. Get the detail and the enhanced status code, then decode it (Step 7).
Pending / Deferred: still in transit or retrying. Usually transient; trace again shortly. A persistent defer points at a recipient-side or connector problem.
For the per-attempt detail behind a Failed or Deferred status, pipe to the detail cmdlet:
Get-MessageTraceV2 -RecipientAddress user@contoso.com -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) | Get-MessageTraceDetailV2 | Select-Object Date, Event, Detail
The trace says Delivered. Where is it actually?
Junk Email folder: the most common answer. The message was delivered but filtered to Junk by the mailbox. Check the user's Junk folder and their blocked/safe sender lists. A bulk or low-confidence-spam verdict does this.
A rule moved it: an inbox rule may have moved or deleted it. List the rules.
Get-InboxRule -Mailbox user@contoso.com | Select-Object Name, Description, Enabled
Deleted or archived: auto-archive or a retention policy may have moved it. Check the online archive and Recoverable Items.
Hidden by focused inbox: it may simply be in Other rather than Focused. Have the user check both.
Inbox rules are the quiet culprit here: a forgotten rule from years ago, or one an attacker created to hide responses during a compromise. A rule that moves mail to a rarely-seen folder or to Deleted Items, especially one that forwards externally, is worth treating as a security signal, not just a tidiness setting.
The Status was FilteredAsSpam or Quarantined. Find it in quarantine:
Get-QuarantineMessage -RecipientAddress user@contoso.com -StartReceivedDate (Get-Date).AddDays(-7) | Select-Object ReceivedTime, SenderAddress, Subject, Type, MessageId
It is in quarantine: review it. If legitimate, release it and, if appropriate, allow the sender.
Get-QuarantineMessage -MessageId "<the-id>" | Release-QuarantineMessage -ReleaseToAll
It was a false positive: the real fix is upstream. Check why it scored as spam by reading the message headers, the spam confidence level (SCL) and the authentication results.
To understand why a message was filtered, paste its headers into the header analyser: it shows the SCL, the spam verdict category and the SPF, DKIM and DMARC results in one view. A legitimate sender failing DMARC is a common cause of false-positive filtering, and the fix is on the sending domain, not your tenant.
Trace by sender this time. Does it show, and what Status?
Get-MessageTraceV2 -SenderAddress user@contoso.com -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) | Select-Object Received, RecipientAddress, Subject, Status
Delivered: Exchange Online handed it off successfully. The message is at the recipient's side; the problem is their filtering or mailbox. They should check their Junk and quarantine. There is nothing more to fix on your end.
Failed: the far end rejected it. An NDR was generated, go to Step 7 with its code.
Does not appear: the user may not have actually sent it, or sent from a different address or device. Confirm the exact From address and that the send completed.
For outbound Delivered cases, resist the urge to keep digging on your side. Once Exchange Online reports Delivered, the message reached the recipient's mail system and the trail continues there, beyond your visibility. The recipient's IT must trace inbound on their side.
You have a Failed status or the user forwarded a bounce. Get the enhanced status code (like 5.7.1 or 5.4.1) and decode it.
Have the NDR text: paste it into the NDR decoder; it extracts the code and explains the likely cause and fix.
Common outbound codes: 5.1.1 recipient does not exist (check the address), 5.7.1 / 5.7.12 blocked or sender not authenticated (recipient restrictions or a group requiring authentication), 5.4.1 / 5.4.6 routing or loop, 5.7.23 / 5.7.26 SPF or DMARC rejection at the far end.
SPF and DMARC rejections (5.7.23, 5.7.24, 5.7.26) point back at your sending domain's authentication records. Paste them into the email auth analyser to check the SPF record for the 10-lookup limit and the DMARC policy. A third-party service sending as your domain but missing from your SPF record is a frequent cause.
If many users or many messages are affected rather than one, widen the lens. A transport rule may be silently dropping or redirecting mail, check the rules and their actions:
Get-TransportRule | Where-Object State -eq "Enabled" | Select-Object Name, Priority, Description
Other tenant-wide causes worth checking when the problem is broad: a connector misconfiguration after a DNS or gateway change (5.4.1 / 5.7.64 territory), a domain whose accepted-domain type is wrong (Authoritative vs Internal Relay), or a recently changed anti-spam or anti-phishing policy catching more than intended. The migration guide covers the audit-log search for "who changed what" if a configuration change is suspected.
Direction first, then trace, then read the Status: Delivered means look in Junk, rules and the archive; Quarantined means check and release; Failed means decode the NDR. Outbound Delivered means the recipient's side owns it. A pattern across users means transport rules, connectors or a policy change. Establish those facts in order and the cause appears faster than any single lucky guess.