A remote move batch in an Exchange hybrid deployment usually just works, until it doesn't: a batch sits at Synced for days, one user shows Failed with a cryptic MigrationPermanentException, or the Data Consistency Score says Investigate and nobody knows what to approve. In this post I'll walk through how a batch actually moves a mailbox, how to read the status objects, the errors Microsoft documents and how to fix them, and what to check once the mailbox lands in Exchange Online.
Symptoms#
- The batch stays at Syncing or Synced and never reaches Completed, or finishes as Synced with errors / Completed with errors.
- Individual users show Failed with messages such as
MigrationPermanentException: The target mailbox doesn't have an SMTP proxy matching 'contoso.mail.onmicrosoft.com', orThe HTTP request is unauthorized with client authentication scheme 'Negotiate'against/EWS/mrsproxy.svc. - The endpoint can't be created at all, or
Test-MigrationServerAvailabilityfails. - Completion is blocked because the Data Consistency Score is Investigate or Poor, and the user has skipped items.
Why it happens#
An onboarding remote move is pulled by the Mailbox Replication Service (MRS) in Exchange Online. It connects through the migration endpoint to the MRS Proxy on your on-premises Mailbox servers (https://mail.contoso.com/EWS/mrsproxy.svc), copies the mailbox in an initial sync, keeps it in step with incremental syncs (roughly every 24 hours) while the user still works on-premises, and only on completion does the final sync, flips the on-premises object to a remote mailbox pointing at the target delivery domain, and sends Outlook to the cloud. A batch is a wrapper around one move request per user, so there are three places where state lives: the batch, the migration user, and the move request. Most "stuck" batches are one of four things: the proxy path (MRS Proxy, authentication, firewall pre-authentication), a recipient object that doesn't line up between the two directories (proxy address, accepted domain, Exchange GUID), a stale move request from an earlier attempt, or data loss the service wants you to acknowledge before it will finalise.
Two things that trip people up: the Exchange Online licence is assigned after the move completes (you then have 30 days), and BadItemLimit and LargeItemLimit are deprecated for moves into Exchange Online. The service now grades each move with a Data Consistency Score: Perfect, Good, Investigate (approval needed to complete) or Poor (can't be forced; involve Microsoft Support). Items are skipped because they're corrupt in the source, larger than the tenant's maximum message size, or missing from the target when the move is ready to complete.
How to fix it#
- Prove the proxy path first. On every on-premises Mailbox server the EWS virtual directory needs the MRS Proxy endpoint on, and the hybrid troubleshooter also expects WS-Security authentication enabled; Exchange Online requires Windows authentication on that endpoint. Then test from Exchange Online PowerShell with the endpoint credentials in
DOMAIN\userformat:A 401 atPowerShell# On-premises Exchange Management Shell Get-WebServicesVirtualDirectory | Format-Table Identity, MRSProxyEnabled, WSSecurityAuthentication Set-WebServicesVirtualDirectory -Identity "EXCH01\EWS (Default Web Site)" -MRSProxyEnabled $true # Exchange Online PowerShell $cred = Get-Credential # CONTOSO\migrationadmin Test-MigrationServerAvailability -ExchangeRemoteMove -RemoteServer mail.contoso.com -Credentials $cred Get-MigrationEndpoint | Format-List Identity, RemoteServer, LastModifiedTimemrsproxy.svcis almost always the credential stored on the endpoint (expired password, wrong format, account without rights) or a reverse proxy pre-authenticating the path. Microsoft's guidance is to publish/ews/mrsproxy.svc,/ews/exchange.asmx/wssecurityand/autodiscover/autodiscover.svcwithout pre-authentication and to exclude Microsoft 365 IP ranges from intrusion-detection flood limits. - Read all three status objects before changing anything.
Batch states you'll see: Stopped (created but not started, or stopped), Starting, Syncing, Synced, Synced with errors, Completing, Completed, Completed with errors, Stopping and Removing;PowerShell
Get-MigrationBatch | Format-List Identity, Status, DataConsistencyScore, TotalCount, SyncedCount, FinalizedCount, FailedCount Get-MigrationUser -BatchId "Finance wave 1" | Format-Table Identity, Status, DataConsistencyScore, ErrorSummary Get-MigrationUserStatistics -Identity adele@contoso.com -IncludeReport | Format-List Status, Error, Report Get-MoveRequest -Identity adele@contoso.com | Get-MoveRequestStatistics -IncludeReport | Format-List Status, StatusDetail, PercentComplete, MessageGet-MigrationBatch -Statusalso accepts Failed, IncrementalSyncing, Waiting and Corrupted. Per user, Queued means the endpoint's connections are all busy, Synced means the initial copy is done and the mailbox is waiting for completion, and Failed can mean either provisioning or the copy failed. Microsoft's own advice is not to start troubleshooting a queued or slow move until it has shown no progress for a long stretch (around eight hours), because moves run at a lower priority than mail flow. - Fix the documented recipient errors.
Running the move directly with
Error or symptom Cause Fix The target mailbox doesn't have an SMTP proxy matching '<tenant>.mail.onmicrosoft.com'The on-premises mailbox lacks the routing address, usually because EmailAddressPolicyEnabledisFalseor the policy template was never updated by the Hybrid Configuration Wizard; or the address exists on-premises but hasn't synced to the mail user in Exchange OnlineAdd the <tenant>.mail.onmicrosoft.comtemplate to the email address policy and apply it, or add the secondary address by hand; checkGet-MailUser | Select -ExpandProperty EmailAddressesin the cloud after the next syncMove won't start, earlier attempt exists A completed or failed move request is still present Get-MoveRequest -Identity user, thenRemove-MoveRequestAddresses rejected during provisioning One of the mailbox's SMTP domains isn't an accepted, verified domain in the tenant (often a .localaddress)Compare (Get-Mailbox user).EmailAddresseson-premises withGet-AcceptedDomainin Exchange Online; add the domain or remove the non-routable addressTarget object mismatch The ExchangeGuidon the on-premises object and the cloud mail user differCompare Get-RemoteMailbox | FL ExchangeGuidwithGet-Mailbox | FL ExchangeGuid; stamp the correct GUID on the mail userCorrupt items, repeated transient failures Item or mailbox-level corruption in the source database Move the mailbox to another on-premises database first, then retry New-MoveRequest -Identity user -Remote -RemoteHostName mail.contoso.com -RemoteCredential $cred -TargetDeliveryDomain contoso.mail.onmicrosoft.comoften returns a more actionable error than the batch does. - Deal with skipped items. List them, decide whether the loss is acceptable, then approve so the batch can finalise:
Approving a batch graded Investigate lets every user graded Perfect, Good or Investigate complete; users graded Poor stay blocked until Microsoft Support looks at them.PowerShell
$stats = Get-MigrationUserStatistics -Identity adele@contoso.com -IncludeSkippedItems $stats.SkippedItems | Format-Table -AutoSize Subject, Sender, DateSent, ScoringClassifications Set-MigrationUser -Identity adele@contoso.com -ApproveSkippedItems # or Set-MigrationBatch -Identity "Finance wave 1" -ApproveSkippedItems - Complete deliberately. If the batch was created without
-AutoComplete, nothing finalises until you runComplete-MigrationBatch -Identity "Finance wave 1"or theCompleteAftertime arrives.CompleteAfteris interpreted as UTC unless you also pass-TimeZone, which is a classic reason for a cutover that happens at the wrong hour. A batch stuck in Completing can often be nudged withGet-MoveRequest | Where-Object {$_.Status -eq "AutoSuspended"} | Resume-MoveRequest.
Verify the fix#
Get-MigrationUsershows Completed andGet-MoveRequestStatisticsshows 100 percent with no skipped items you haven't approved.- On-premises,
Get-RemoteMailbox adele | Format-List RemoteRoutingAddress, ExchangeGuidreturns the mail.onmicrosoft.com address; in the cloud,Get-Mailbox adele | Format-List RecipientTypeDetails, Databaseshows a real mailbox. - Assign the licence, have the user restart Outlook (it picks up the new location through Autodiscover) and send a test message in each direction, confirming with
Get-MessageTraceV2. - Remove the completed batch; Microsoft recommends this to avoid errors if the same users are ever moved again. Unattended Synced batches are stopped after 60 days and completed ones are removed after 60 days anyway.
Prevent it next time#
- Run
Test-MigrationServerAvailabilityand a one-mailbox pilot batch before every wave; endpoint credentials expire quietly. - Audit
EmailAddressPolicyEnabledand the routing address across all mailboxes in the wave with a singleGet-Mailbox -Filterbefore you upload the CSV. - Move delegates together with the mailboxes they access; some cross-premises permissions don't work the way they do within one organisation.
- Decide the cutover time in UTC, write it into
-CompleteAfterwith-TimeZone, and tell the users.