TooManyBadItemsPermanentException means a mailbox move hit more bad items than it was allowed to skip, so the Mailbox Replication service stopped the move. In hybrid moves to Exchange Online, the bad items are usually mailbox or folder permissions that point to a user or group that can't be resolved, not damaged messages. Read the bad items from the move report, fix or remove the unresolvable permissions and resubmit the move, or, where the item loss is acceptable, review and approve the skipped items so the Data Consistency Score lets the migration complete.
Who this is for and what you will have at the end
This guide is for Exchange administrators moving mailboxes between Exchange Server and Exchange Online with remote move migrations, typically in a hybrid deployment. Microsoft notes that the same guidance may also apply to the closely related TooManyMissingItemsPermanentException, because missing items are counted in the bad item limit.
By the end you will have:
- The move report for the failed user and a list of its bad items, grouped by kind.
- A clear decision for each mailbox: fix permissions and resubmit, repair the source, or approve the skipped items.
- A completed move and a check that nothing important was left behind.
For the bigger picture of planning moves between organizations, see the cross-tenant migration architecture post.
What counts as a bad item
The cmdlet reference defines a bad item as a corrupt item in the source mailbox that can't be copied to the target mailbox. Missing items are counted too: items in the source that can't be found in the target when the request is ready to complete.
Permissions joined that list in 2016. A move runs in stages: it creates the folder hierarchy, copies the data (the initial sync), and then copies rules and security descriptors. Security descriptors are the access control lists on the mailbox and on each folder. About midway through 2016, Exchange Online started marking a security principal that it couldn't validate or map to an Exchange Online object as a bad item. Before that, invalid permissions were silently dropped. Every unresolvable entry now increments the bad item count, so a mailbox with years of permissions for departed staff can fail even though every message copied cleanly.
Only explicit mailbox permissions are copied, such as Full Access granted with Add-MailboxPermission. Inherited permissions, for example a Receive-As grant on a database, aren't evaluated.
To resolve a permission, Exchange Online sends the SID to the on-premises MRSProxy, which looks it up in Active Directory and returns attributes including the legacyExchangeDN. Exchange Online then looks for a cloud recipient with that value stamped as an X500 proxy address. If any step fails, the entry becomes a bad item. The move report records it as one of two kinds:
| Report entry | Meaning | Typical cause |
|---|---|---|
Unable to translate principals ... Failed to find a principal from the source forest (SourcePrincipalMapping) | The SID couldn't be resolved in on-premises Active Directory. | The user was deleted, or the SID exists only in SIDHistory, which MRSProxy doesn't search. |
Failed to find a principal in the target forest that corresponds to the following source forest principal values (TargetPrincipalMapping) | The principal exists on-premises but has no matching object in Exchange Online. | The user or group is outside the directory synchronization scope, or it's a security group that isn't mail-enabled, which isn't synchronized to Exchange Online. |
Exchange Online has a separate built-in bad item limit of 1,000 for source principal mapping errors, so those alone fail a move only when there are more than 1,000 of them. A move that fails on permissions therefore points either to a very large number of stale entries or to target mapping errors.
BadItemLimit versus the Data Consistency Score
Older guidance, including the Microsoft support article for this error, tells you to raise BadItemLimit on the move request and resume it. Check which environment you're in before you follow that:
| Where the move request lives | How bad items are handled |
|---|---|
| Exchange Online (hybrid onboarding and offboarding batches, move requests created in Exchange Online PowerShell) | The cmdlet reference lists BadItemLimit and LargeItemLimit as available only in on-premises Exchange and deprecated in the cloud-based service. Admins review the Data Consistency Score and approve skipped items before the migration completes. |
| On-premises Exchange Server (local moves between databases) | BadItemLimit is still a valid parameter. The default is 0; Microsoft recommends 10 or lower if you accept leaving a few items behind. |
The Data Consistency Score has four grades:
| Grade | Meaning | Approval |
|---|---|---|
| Perfect | No inconsistencies. | Not required. |
| Good | At least one inconsistency, but not impactful, such as lost metadata or folder permissions. | Not required. |
| Investigate | A small amount of noticeable data loss. | Required for remote move, public folder and Google Workspace migrations. |
| Poor | Major data loss. | Contact Microsoft Support. Approval can't force completion. |
A batch's score equals the worst score of any user in it. A migration user held back for this reason reports an error such as The data consistency score (Investigate) for this request is too low.
Prerequisites
- Exchange Online PowerShell with permissions to manage migrations:
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com- Exchange Management Shell access on-premises, to change permissions and run repairs on the source mailbox.
- The identity of the failed user. The examples use
ana@contoso.comand a batch namedBatch-Finance.
Step 1: Find the failed user and capture the move report
List failed users and their errors:
Get-MigrationUser -Status Failed | Format-List Identity, ErrorSummary
Get-MigrationUserStatistics -Identity ana@contoso.com -IncludeReport | Format-List Status, Error, ReportThen save the full move report. Working from a saved copy lets you analyze it in a plain PowerShell window without a live connection, and it's the file Microsoft Support asks for:
Get-MoveRequestStatistics -Identity ana@contoso.com -IncludeReport |
Export-Clixml C:\Temp\ana-movereport.xml
$movereport = Import-Clixml C:\Temp\ana-movereport.xmlIf the move request was already removed but the mailbox exists, the support article for this error reads the report from the mailbox's move history instead:
$stats = Get-MailboxStatistics ana@contoso.com -IncludeMoveReport
$report = $stats.MoveHistory[0].Report
$report.BadItemsStep 2: Classify the bad items
List the bad items and open them in a grid you can filter:
$movereport.Report.BadItems | Out-GridViewIn the grid, select Add criteria, choose Kind, set the condition to does not contain and type Security. If the list is now empty, every bad item is a permissions problem and no message content is at risk. If entries remain, you also have corrupt, large or missing items.
To check the overall failure pattern in the same report:
$movereport.Report.Failures | Group-Object FailureType | Format-Table -AutoSize
$movereport.Report.Failures[-1]For skipped items at the migration-user level, including the scoring classification for each one:
$userStats = Get-MigrationUserStatistics -Identity ana@contoso.com -IncludeSkippedItems
$userStats.SkippedItems | Format-Table -AutoSize Subject, Sender, DateSent, ScoringClassificationsThe same list is in the Exchange admin center: go to Migration, select the batch, select View details, select the user, and then select Skipped item details.
Step 3: Apply the fix that matches the bad items
Permissions: fix them on-premises and submit a new move
The Exchange Team's recommended approach is to keep bad item tolerance low and remediate the mailboxes that fail, because a failure means either a very large number of stale permissions or valid permissions that aren't mapping to Exchange Online. Both deserve a look.
- From the report entries, note the SIDs, aliases or display names that couldn't be translated, and the folder names for folder-level entries.
- On-premises, review the mailbox and folder permissions with
Get-MailboxPermissionandGet-MailboxFolderPermission. - For source mapping errors (deleted accounts), remove the stale entries with
Remove-MailboxPermissionorRemove-MailboxFolderPermission. - For target mapping errors, decide whether the principal should exist in Exchange Online. If a user is outside the synchronization scope, bring it into scope. If permissions were granted to a security group that isn't mail-enabled, remember that such groups aren't synchronized to Exchange Online; grant the access to a principal that is synchronized, or remove the entry.
- Remove the failed move and submit a new one.
The last step matters. Permissions are evaluated only once, at the end of the initial data copy. Fixing them while the failed move request still exists doesn't help that request; you have to remove it and start again:
Remove-MigrationUser -Identity ana@contoso.comThen add the user to a new batch, or, for a single mailbox, create a new remote move request from Exchange Online PowerShell.
Corrupt items: repair or relocate the source mailbox
When the remaining bad items aren't permissions, try to clean the source first. Microsoft's hybrid troubleshooter notes that corrupt items or mailboxes can often be fixed by moving the mailbox between two on-premises mailbox databases before moving it to Exchange Online. On Exchange Server, New-MailboxRepairRequest can also detect and repair some mailbox corruption:
New-MailboxRepairRequest -Mailbox ana@contoso.com -CorruptionType MessageId
Get-MailboxRepairRequest -Mailbox ana@contoso.comRun these in the Exchange Management Shell on-premises; they aren't available in Exchange Online.
Accept the loss: approve skipped items
If you have reviewed the skipped items and the loss is acceptable, approve them so the migration can complete. For a migration user or a whole batch:
Set-MigrationUser -Identity ana@contoso.com -ApproveSkippedItems
Set-MigrationBatch -Identity Batch-Finance -ApproveSkippedItemsApproveSkippedItems marks all skipped items discovered before the current time as approved. For a move request you manage directly, use the approval time instead:
Set-MoveRequest -Identity ana@contoso.com -SkippedItemApprovalTime $(Get-Date).ToUniversalTime()Approving a batch scored Investigate lets every user scored Perfect, Good or Investigate complete. In a batch scored Poor, approval still completes those users but never a user scored Poor. Those need Microsoft Support.
For Google Workspace migrations, where both mailboxes stay live, new skipped items can appear after you approve, so you may need to approve more than once.
On-premises local moves: raise BadItemLimit
For a move request in on-premises Exchange Server, the classic fix still applies. Keep the number as low as the report justifies:
Set-MoveRequest -Identity ana@contoso.com -BadItemLimit 10
Resume-MoveRequest -Identity ana@contoso.comIn Exchange Server 2010, values of 51 or higher also require -AcceptLargeDataLoss.
Verification
Get-MigrationUser -Identity ana@contoso.com | Format-List Status, DataConsistencyScore
Get-MigrationUserStatistics -Identity ana@contoso.com | Format-List SkippedItemCount, SkippedItems
Get-MigrationBatch -Identity Batch-Finance | Format-List Status, DataConsistencyScoreYou want the user to reach Synced or Completed, the score to be Perfect, Good or an approved Investigate, and the skipped item count to match what you reviewed. After completion, ask delegates of the migrated mailbox to confirm that shared folder access still works; permissions that were dropped as bad items have to be re-granted in Exchange Online.
If the batch was set to complete automatically but stays incomplete, a low Data Consistency Score is a common reason. Approve the skipped items and complete the batch.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
TooManyBadItemsPermanentException, report full of Unable to translate principals | Unresolvable mailbox or folder permissions | Clean up permissions on-premises, remove the migration user, submit a new move. |
| Same error after fixing permissions and resuming | Permissions are evaluated once per move | Remove the move and create a new one. |
TooManyMissingItemsPermanentException | Items missing from the target at completion count as bad items | Review skipped items; approve if acceptable. |
The data consistency score (Investigate) for this request is too low | Skipped items need approval | Review, then Set-MigrationUser -ApproveSkippedItems. |
| Grade Poor | Major data loss detected | Contact Microsoft Support; approval doesn't apply. |
Older guidance says to raise -BadItemLimit on an Exchange Online move | The parameter is documented as deprecated in the cloud service | Use skipped-item review and approval instead. |
| Bad items that aren't permissions | Item-level corruption in the source | Move between on-premises databases or run New-MailboxRepairRequest, then retry. |
Microsoft documents a MoveOptions parameter on New-MoveRequest for skipping move stages such as folder ACLs, but its reference says not to use it unless Microsoft Support or specific documentation directs you to.
Checklist
- Save the move report with
Export-Clixmlbefore changing anything. - Group bad items by Kind; separate permissions from content.
- Permissions: remove stale entries, fix sync scope, avoid non-mail-enabled security groups, then resubmit the move.
- Content: relocate or repair the source mailbox, then retry.
- Review skipped items and approve only what you accept losing.
- Escalate a Poor grade to Microsoft Support.
- After completion, re-grant any permissions that were dropped.
References
- The number of bad items exceeds the set limit
- Track and prevent migration data loss in Exchange Online
- Troubleshoot migration issues in Exchange hybrid
- TooManyBadItemsPermanentException error when migrating to Exchange Online (Exchange Team Blog)
- Troubleshooting Failed Migrations (Exchange Team Blog)
- Choosing and Troubleshooting Exchange Online Mailbox Migrations (Exchange Team Blog)
- Set-MoveRequest
- Set-MigrationBatch
- Set-MigrationUser
- Get-MigrationUserStatistics
- Get-MoveRequestStatistics
- Get-MailboxStatistics
- New-MoveRequest