A migration batch that shows Syncing for days is rarely hung as a whole. The batch status only summarizes its users, and the usual culprits are individual mailboxes that are queued behind the endpoint's concurrency limit, stalled by throttling on the source or in Exchange Online, waiting for skipped-item approval, or failed and never retried. Find the users that aren't progressing with Get-MigrationUser and Get-MigrationUserStatistics, read the stall reason in the move report, and then fix that specific cause.
Who this is for and what you will have at the end
This guide is for Exchange and Microsoft 365 administrators running native migration batches into Exchange Online: hybrid remote moves from Exchange Server, cutover and staged migrations, IMAP migrations and Google Workspace migrations. It assumes the batch was created successfully and has started, but progress has stopped or slowed to a crawl.
By the end you will have:
- A list of the exact mailboxes holding the batch in Syncing, with their status and error.
- The stall reason and where the time went, for move-request based migrations.
- The fix that matches each cause, and a way to verify the batch is moving again.
If you are still planning the project, the end-to-end flow for Gmail sources is covered in the Google Workspace to Microsoft 365 migration guide, and tenant-to-tenant moves are covered in the cross-tenant migration architecture post.
What Syncing actually means
The Migration dashboard in the Exchange admin center shows one status per batch. The important ones for this problem are:
| Batch status | Meaning |
|---|---|
| Syncing | The batch was started and mailboxes in it are being actively migrated. You can stop it. |
| Synced | The batch finished the initial sync. For most migration types, mailboxes are then synchronized every 24 hours during incremental sync. |
| Synced with errors | Initial sync finished but some mailboxes failed. Successful mailboxes keep their 24-hour incremental sync. |
| Stopped | The batch was never started, or it was stopped after running for a while. |
| Completed | The batch is complete. |
At the user level there is more detail. Get-MigrationUser -Status accepts values such as Queued, Provisioning, Syncing, Synced, IncrementalSyncing, Failed, IncrementalFailed, Completing and CompletionFailed. A user in Queued is in a running batch but hasn't started, which typically happens when all the connections on the migration endpoint are in use.
So a batch stuck on Syncing means at least one user hasn't reached Synced. Your first job is to find out which ones and why.
Also be aware of the housekeeping rules: batches with a status of Synced and no administrator activity for 60 days are stopped, batches with Stopped or Failed status are removed after 90 days, and Completed batches are removed after 60 days.
Prerequisites
- An account with the Exchange permissions needed to run the migration cmdlets in Exchange Online.
- The Exchange Online PowerShell module. Connect with:
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com- For hybrid moves, access to the Exchange Management Shell on the on-premises servers that host the MRSProxy endpoint, because many stalls originate there.
- The name of the batch. The examples below use
Batch-Finance.
Step 1: Find the users that are holding the batch
Start with the batch itself and then break it down per user:
Get-MigrationBatch -Identity Batch-Finance -IncludeReport | Format-List
Get-MigrationUser -BatchId Batch-Finance | Get-MigrationUserStatisticsThe second command lists every user in the batch with its current status. Group the users into four buckets:
- Queued: waiting for a migration slot. This is a concurrency question (Step 3, first section).
- Syncing with no change in synced data between runs a few hours apart: stalled. Read the stall reason (Step 2).
- Failed: the user failed and the batch is still running for everyone else. Read the error and retry.
- Synced: done with initial sync, nothing to fix.
For any user that looks wrong, pull the detailed report:
Get-MigrationUserStatistics -Identity ana@contoso.com -IncludeReport |
Format-List Status,Error,ReportThe same detail is in the Exchange admin center. Go to Migration, select the batch, select View details under Migration details, and then select a user. The details pane shows the status, data migrated, migration rate, error, last successful sync date and a Download the report for this user link that you can hand to Microsoft Support.
Step 2: Read the stall reason and where the time went
For hybrid remote moves, every migration user has a move request, and the move request statistics record how long the move spent in each state. The Exchange Team's guidance is to capture the diagnostic information and look at the Durations section:
$stats = Get-MoveRequestStatistics -Identity ana@contoso.com -DiagnosticInfo "verbose,showtimeslots,showtimeline"
$stats.DiagnosticInfoThe Get-MoveRequestStatistics reference lists ShowTimeline, ShowTimeslot and Verbose as the values for -DiagnosticInfo in Exchange Online, and notes that the parameter is typically used at the request of Microsoft Support. Export the result if you want to analyze it offline or attach it to a support case:
Get-MoveRequestStatistics -Identity ana@contoso.com -IncludeReport -DiagnosticInfo Verbose |
Export-Clixml C:\Temp\ana-movereport.xmlA quicker view of throttling comes from the report itself. The TargetThrottles and SourceThrottles properties show how long the move was held back by each resource:
$r = Get-MoveRequestStatistics -Identity ana@contoso.com -IncludeReport
$r.Report.TargetThrottles
$r.Report.SourceThrottlesThe status detail tells you which side is stalling. In an onboarding move (on-premises to Exchange Online) the source is your on-premises environment and the target is Exchange Online:
| Stall reason | Side in an onboarding move | What it means |
|---|---|---|
| StalledDueToTarget_MdbAvailability | Exchange Online | The replay queue of transaction logs into remote database copies is too large. Lasts until it drains. |
| StalledDueToTarget_MdbReplication | Exchange Online | The copy queue of transaction logs to other DAG members is too large. |
| StalledDueToTarget_DiskLatency | Exchange Online | Disk latency on the target database is at a level where more I/O could affect users. |
| StalledDueToTarget_Processor | Exchange Online | CPU on the target MRS servers is too high. |
| StalledDueToTarget_BigFunnel | Exchange Online | Content indexing had a temporary interruption and is recovering. |
| StalledDueToSource_UnknownReason | On-premises | The source didn't pass a specific reason. Most often a content indexing problem on the on-premises servers. |
| StalledDueToSource_MailboxLock | On-premises | A transient communication error between cloud MRS and on-premises MRSProxy left the mailbox locked. |
| StalledDueToSource_EndpointCapacityExceeded | Exchange Online endpoint settings | More moves are running than the migration endpoint's concurrency allows. |
Step 3: Apply the fix that matches the cause
Users stuck in Queued: raise endpoint concurrency carefully
For IMAP, cutover and staged migrations, migration-service throttling limits how many mailboxes migrate at once. The default is 20 mailboxes across all batches, and you can raise it to a maximum of 100:
Get-MigrationEndpoint -Identity OnboardingME01 | Format-List
Set-MigrationEndpoint -Identity OnboardingME01 -MaxConcurrentMigrations 40MaxConcurrentIncrementalSyncs must be less than or equal to MaxConcurrentMigrations. After changing the endpoint, run Set-MigrationBatch -Identity Batch-Finance -Update. The Update switch triggers the migration service to reapply all of the settings from the endpoint, batch and user to the migration process.
Raise the number gradually. Microsoft's advice is to start with a small value, for example 10, and increase it while watching the source system, because a source without enough resources affects your users before it speeds up the migration.
For hybrid moves, StalledDueToSource_EndpointCapacityExceeded appears when the endpoint values are lower than the number of mailboxes you are moving at once. The tenant-level maximum for hybrid remote moves is 300 by default and is visible in Get-MigrationConfig. Microsoft Support can raise it (up to 1,000) when you publish multiple MRSProxy endpoints, but only with a business justification, because higher concurrency loads your on-premises servers.
StalledDueToTarget: wait, and know when to escalate
Exchange Online throttles discretionary workloads such as mailbox moves so that the service keeps running well for all users, and hybrid moves are stalled when resource health drops toward the throttling threshold. Seeing target stalls is normal. There are no throttling policies that you or Microsoft Support can adjust to speed up hybrid moves, and changing EWS throttling has nothing to do with MRS-based migrations.
Microsoft considers it a service problem only when a move exceeds the 90th percentile of its published duration estimates and most of the time is spent in StalledDueToTarget. For onboarding from on-premises Exchange, the published P90 is 6 days for mailboxes of 10 to 50 GB and 13 days for 50 to 100 GB. If you are past that and the Durations section shows the target side as the main consumer, open a case and attach the exported move report.
StalledDueToSource: fix the on-premises side
Source stalls are yours to fix. Look in the verbose diagnostic info for a message that names the unhealthy resource. A content indexing problem looks like The remote server is unhealthy due to 'Resource 'CiAgeOfLastNotification(...)' is unhealthy..., and a CPU problem names the Processor resource.
- Content indexing: when the verbose diagnostic info names a
CiAgeOfLastNotificationresource, the content index of the named source database is unhealthy. Repair content indexing on that database before expecting the moves to speed up. - CPU: add CPU or more migration servers. If a server is used only for migrations, the Exchange Team documents a setting override that disables processor throttling for MRS, but don't use it on servers that also host production mailboxes.
- Mailbox lock: transient timeouts between Exchange Online MRS and on-premises MRSProxy leave the source mailbox locked for roughly the TCP keep-alive time of the Windows Server (two hours by default). The Exchange Team's general recommendation is to lower the TCP keep-alive time to 5 minutes so moves recover faster, but that doesn't fix the timeouts themselves. Bypass load balancers, IDS and reverse proxies in front of the MRSProxy servers where you can, decrease the
MaxConcurrent*values on the hybrid migration endpoint, and improve server performance and bandwidth. - High latency with many folders: finalization verifies content per folder, so a mailbox with thousands of folders over a high-latency link can stay locked in final sync for a long time. Check
Report.SessionStatistics.SourceLatencyInfo.Averagein the move report; the recommended maximum is 300 ms.
When Exchange Server 2019 is the source or the target of the moves, seeing only 10 mailboxes in CopyingMessages with the rest in stalled states such as StalledDueToTarget_MdbReplication is expected: workload management limits moves from the same source or to the same target to 10 at a time by default. Microsoft documents New-SettingOverride commands to raise the limit, starting at 25 and not going above 100.
Google Workspace and IMAP sources: the source throttles too
For Gmail and other IMAP sources, the data source often limits how much data can be extracted in a period, so throughput drops no matter what you set in Exchange Online. Raising endpoint concurrency beyond what the source accepts only produces more throttled connections. Keep batches smaller and let incremental syncs catch up.
Failed users inside a Syncing batch: retry them
Start-MigrationBatch resumes a stopped batch and retries failures in a Failed or Synced with Errors batch. In Exchange Online it can be run at any time to retry failed users within the batch:
Start-MigrationBatch -Identity Batch-FinanceIf one mailbox repeatedly fails or stalls while the rest finish, remove it with Remove-MigrationUser -Identity ana@contoso.com and migrate it in a batch of its own, so it can't hold the rest of the users.
Skipped items waiting for approval
Every migration produces a Data Consistency Score. A batch's score equals the worst score of any user in it. For remote move, public folder and Google Workspace migrations, a grade of Investigate requires you to approve skipped items before the migration can complete:
Get-MigrationUserStatistics -Identity ana@contoso.com -IncludeSkippedItems |
Select-Object -ExpandProperty SkippedItems |
Format-Table Subject,DateReceived -AutoSize
Set-MigrationUser -Identity ana@contoso.com -ApproveSkippedItems
# or for every user in the batch
Set-MigrationBatch -Identity Batch-Finance -ApproveSkippedItemsA grade of Poor can't be forced through; contact Microsoft Support. Note that the BadItemLimit and LargeItemLimit parameters of Set-MigrationBatch are available only in on-premises Exchange, so in Exchange Online skipped-item approval is the mechanism, not raising a limit.
Verify that the batch is moving again
Run these checks a few hours apart:
Get-MigrationUser -BatchId Batch-Finance -Status Queued
Get-MigrationUser -BatchId Batch-Finance -Status Failed
Get-MigrationUser -BatchId Batch-Finance | Get-MigrationUserStatistics
Get-MigrationBatch -Status SyncingYou want the Queued and Failed lists to shrink, the data migrated for users still in Syncing to grow (the Data migrated and Last successful sync date fields in the EAC user details are the easiest place to see it), and the batch to reach Synced or Synced with errors. After that, incremental sync runs every 24 hours. If you need a fresh delta just before cutover, Set-MigrationBatch -Identity Batch-Finance -SyncNow starts an immediate sync for users already in Synced status (it doesn't resume Failed users).
Troubleshooting: other symptoms that look like a stuck batch
| Symptom | Cause | Fix |
|---|---|---|
| Batch shows Synced for weeks and never completes | Completion was never triggered | Run Complete-MigrationBatch -Identity Batch-Finance, or set -CompleteAfter. |
Batch appears stuck after running Complete-MigrationBatch | Unapproved skipped items, or repeated completion requests within 8 hours aren't reprocessed | Approve skipped items with Set-MigrationUser -ApproveSkippedItems. |
Move fails straight away with SourceMailboxAlreadyBeingMovedTransientException after you recreate it | A leaked MRSProxy session still holds the source mailbox lock | Changing TCP keep-alive doesn't help here. Restart the Mailbox Replication service on the on-premises server hosting the mailbox (Exchange 2013 or later). |
| User sees a MailboxInTransit exception in Outlook on the web during final sync | The source mailbox is locked while content verification runs | Expected during finalization; complete large-folder mailboxes outside working hours. |
| Synced batch changed to Stopped on its own | No administrator activity for 60 days | Resume the batch and complete it. |
| Grade Poor on the Data Consistency Score | Major data loss detected | Contact Microsoft Support; approval doesn't apply. |
Checklist
- Break the batch down per user before changing anything.
- Read the stall reason and the
Durationssection for hybrid moves. - Queued users: raise
MaxConcurrentMigrationsin small steps and apply withSet-MigrationBatch -Update. - Target stalls: wait, compare against the published P90 durations, escalate with an exported report.
- Source stalls: fix content indexing, CPU, network devices and latency on-premises.
- Failed users: retry with
Start-MigrationBatch; isolate repeat offenders in their own batch. - Investigate grade: review and approve skipped items before completion.
- Complete the batch explicitly once every user is Synced.
References
- Manage migration batches in Exchange Online
- Migration users status report in Exchange Online
- Track and prevent migration data loss in Exchange Online
- Microsoft 365 and Office 365 migration performance and best practices
- Troubleshooting Slow Migrations (Exchange Team Blog)
- Mailboxes are stalled during a migration
- Get-MigrationUserStatistics
- Get-MigrationUser
- Get-MigrationBatch
- Get-MoveRequestStatistics
- Set-MigrationBatch
- Set-MigrationEndpoint
- Start-MigrationBatch
- Complete-MigrationBatch
- Connect to Exchange Online PowerShell