Skip to main content

Raajshekhar Rajan

·16 min read

Troubleshooting Guide: Top 30 Errors During Dynamics 365 On-Prem → Online Migrations (OP2OL)

Migrating from Microsoft Dynamics 365 On-Premise to Online (OP2OL) involves complex data transformation. This guide serves as a technical reference for the 30 most common migration failures, including specific error codes (0x8004...), SQL transaction logs, and API throttling limits.

Cover_Image

Section 1: Critical Telemetry & Benchmarks

Use these verified ranges to configure your ETL tools (ADF, KingswaySoft, SSIS).

Metric

Safe Threshold

Failure Threshold

Context

SQL Timeout

< 120 Seconds

> 120 Seconds

Hard limit for TDS/SQL connections. Source: TDS Limits

Batch Size (Complex)

250 - 500 records

> 1,000 records

For Quotes, Orders, Cases (Entities with heavy plugin logic).

Batch Size (Simple)

1,000 - 2,000 records

> 5,000 records

For Tasks, Contacts, Leads.

Concurrency

2 - 4 Threads

> 52 Threads

Per User Limit. Source: Service Protection API

Attachment Size

< 25 MB

> 35 MB

Hard Exchange Online limit. Source: Exchange Online Limits

Section 2: The Master Error Database (01–30)

01. System.TimeoutException (SQL Execution Limit)

  • Category: Infrastructure / SQL
  • Root Cause: The query execution exceeded the hard 2-minute (120s) limit on the Dataverse SQL layer. This frequently occurs when reading "heavy" entities like Annotation (Notes) or ActivityPointer where table sizes exceed 1GB.
  • Official Citation: Dataverse SQL Query Limitations (2-minute limit)
  • Technical Resolution: Do not use standard OData (/api/data/v9.2/) for bulk extraction of >500k rows. Use the TDS Endpoint (SQL) with strict pagination.
  • Reproducible Artifact (T-SQL):
1-- BAD: SELECT * FROM [Annotation] (Will Timeout) 2-- GOOD: Deterministic Pagination using OFFSET/FETCH 3SELECT [AnnotationId], [Subject], [NoteText], [CreatedOn] 4FROM [Annotation] 5WHERE [CreatedOn] >= '2023-01-01' 6ORDER BY [CreatedOn] ASC 7OFFSET 0 ROWS FETCH NEXT 5000 ROWS ONLY;
  • Verification: Run the query in SSMS connected to the TDS endpoint. If execution time > 55s, reduce batch size to 2,000.

02. Generic Plugin Error (0x80040216)

  • Category: Server-Side Logic
  • Root Cause: A synchronous plugin registered on Pre-Operation or Post-Operation is failing. This blocks the transaction.
  • Official Citation: Bypass Custom Business Logic
  • Technical Resolution: Do not disable plugins manually. Use the BypassCustomPluginExecution parameter in your API calls. This requires the migration user to have the prvBypassCustomPluginExecution privilege.
  • Reproducible Artifact (HTTP Header):
1POST [Organization URI]/api/data/v9.2/accounts HTTP/1.1 2MSCRM.BypassCustomPluginExecution: true 3Content-Type: application/json 4 5{ 6 "name": "Migrated Account", 7 "creditlimit": 10000 8}
  • Verification: Check the Plugin Trace Log table in Dataverse. If the bypass is working, no logs will be generated for the migration user during the load.

03. Record Is Unavailable (Referential Integrity / Circular Reference)

  • Category: Data Mapping
  • Root Cause: Record A references Record B, but Record B has not been created yet. Common in "Parent Account" scenarios where Account X is the parent of Account Y, but Account Y is processed first.
  • Technical Resolution: Implement Multi-Pass Ingestion.
    1. Pass 1 (Create): Insert all records with Lookup fields set to NULL.
    2. Pass 2 (Update): Iterate through the source data again and update only the Lookup fields.
  • Reproducible Artifact (C# Logic):
1// Pass 1: Create Stub 2Entity account = new Entity("account"); 3account["name"] = "Child Branch"; 4account["parentaccountid"] = null; // Intentional Null 5Guid createdId = service.Create(account); 6 7// Pass 2: Patch Relationship (After all accounts exist) 8Entity patch = new Entity("account"); 9patch.Id = createdId; 10patch["parentaccountid"] = new EntityReference("account", parentGuid); 11service.Update(patch);
  • Telemetry Threshold: If "Missing Reference" errors exceed 5% of a batch, pause and verify your load order (e.g., Users > Accounts > Contacts).

04. Generic SQL Error (Transaction Log Full)

  • Category: Database Constraints
  • Root Cause: Massive bulk inserts are filling the SQL transaction log buffer on the target instance before a commit can occur.
  • Official Citation: Azure SQL Resource Limits (DTU-based)
  • Technical Resolution: Dataverse does not allow you to change the Recovery Model to "Simple." You must control this via client-side batching. Reduce your ExecuteMultipleRequest batch size.
  • Benchmark:
    • Safe: 10 requests per batch.
    • Risky: > 1000 requests per batch (High risk of log overflow).
  • Verification: Monitor the Dataverse Analytics dashboard > "SQL Failures."

05. ISV Code Aborted (Legacy Assembly)

  • Category: Legacy Extensibility
  • Root Cause: Custom Workflow Activities or Plugins are using unsupported .NET Framework versions (e.g., 4.5.2) or deprecated Xrm.Page references.
  • Official Citation: Supported Extensions & .NET Framework
  • Technical Resolution: Recompile all custom assemblies to .NET 4.6.2 (or 4.7.1) before migration.
  • Verification: Run the Power Apps Solution Checker on your solution zip file. It will flag Type or namespace not found errors for deprecated code.

06. Service Protection Limit (429 Too Many Requests)

  • Category: API Throttling
  • Root Cause: The migration user has exceeded 6,000 requests within a 5-minute sliding window.
  • Official Citation: Service Protection API Limits
  • Technical Resolution: Do not use a fixed Thread.Sleep(). You must parse the Retry-After header returned by the 429 response.
  • Reproducible Artifact (C# / .NET SDK):
1try { 2 service.Execute(request); 3} 4catch (FaultException<OrganizationServiceFault> ex) { 5 if (ex.Detail.ErrorCode == -2147015902) { // 429 Code 6 // Extract the 'Retry-After' duration mandated by the server 7 int retrySeconds = 5; // Default fallback 8 if (ex.Detail.ErrorDetails.Contains("Retry-After")) { 9 retrySeconds = Convert.ToInt32(ex.Detail.ErrorDetails["Retry-After"]); 10 } 11 Thread.Sleep(retrySeconds * 1000); 12 // Retry logic here... 13 } 14}

07. Principal with Id Does Not Exist

  • Category: Security / User Mapping
  • Root Cause: The OwnerId (User or Team GUID) in the source data does not exist in the target Entra ID (Azure AD).
  • Technical Resolution: Create a User Mapping Table. Do not attempt to migrate users with their old On-Prem GUIDs unless you have pre-seeded the target with those exact GUIDs (which is rare).
  • Artifact (Mapping Strategy): | Source GUID | Target GUID | Source Name | Target Name | | :--- | :--- | :--- | :--- | | 1111-aaa... | 2222-bbb... | John Doe | John Doe | | 3333-ccc... | 9999-zzz... | (Terminated User) | "Stub User" / Admin |
  • Verification: Perform a WhoAmI request or simple Retrieve on the target systemuser table using the Target GUID before starting the batch.

08. Invalid Argument (Picklist / OptionSet)

  • Category: Data Schema
  • Root Cause: The source data contains an integer value (e.g., 999) that is not defined in the target OptionSet metadata.
  • Technical Resolution:
    • Option A: Map invalid values to a "Default" (e.g., 999 -> 1).
    • Option B (Admin): Programmatically create the missing option using InsertOptionValueRequest.
  • Reproducible Artifact (SDK Request):
1var request = new InsertOptionValueRequest { 2 AttributeLogicalName = "industrycode", 3 EntityLogicalName = "account", 4 Value = 999, // The missing value 5 Label = new Label("Legacy Industry", 1033) 6}; 7service.Execute(request);

09. Attachment Size Exceeds Limit

  • Category: File Storage
  • Root Cause: Files in Annotation or ActivityMimeAttachment exceed the default 5MB limit.
  • Official Citation: Exchange Online Message Limits
  • Technical Resolution:
    1. Update System Settings > Email > Max file size to 131,072 KB (128 MB).
    2. For files > 128 MB, migrate to Azure Blob Storage and leave a URL link.
  • Verification: Attempt to upload a 10MB file via the Unified Interface. If it fails, the setting has not propagated.

10. Duplicate Key Violation

  • Category: SQL Constraints
  • Root Cause: Attempting to insert a record with a Primary Key (GUID) that already exists in the target.
  • Technical Resolution: Switch from CreateRequest to UpsertRequest. This operation checks for existence: if the record exists, it updates it; if not, it creates it.
  • Reproducible Artifact (C#):
1var account = new Entity("account", new Guid("source-guid-here")); 2account["name"] = "Updated Name"; 3 4var request = new UpsertRequest { Target = account }; 5var response = (UpsertResponse)service.Execute(request); 6 7// response.RecordCreated tells you if it was an Insert or Update

11. Privilege Denied (SecLib::AccessCheckEx Failed)

  • Category: Security Roles
  • Root Cause: The Application User or Service Principal performing the migration lacks the specific privilege (e.g., prvCreateAccount or prvAppendTo) for the entity being migrated.
  • Official Citation: Dataverse Security Roles & Privileges
  • Technical Resolution: Do not rely on a custom "Migration Role." Assign the standard System Administrator role to the Application User. If using a Service Principal, ensure the Application User is enabled and the Client Secret has not expired.
  • Verification: Navigate to Power Platform Admin Center > Environments > Users. Select the Application User and click "Manage Roles" to confirm "System Administrator" is checked.

12. Max Depth Exceeded (Infinite Loop)

  • Category: Workflow / Business Logic
  • Root Cause: A synchronous workflow or plugin is triggering itself recursively. Dataverse enforces a hard "Max Depth" limit (default is 8) to prevent infinite loops.
  • Official Citation: Message Processor & Max Depth
  • Technical Resolution: You must deactivate all workflows and Power Automate flows triggered by "Record Creation" or "Record Update" on the target entities before migration starts.
  • Reproducible Artifact (PowerShell):
1# Use the Microsoft.Xrm.Data.PowerShell module to disable workflows 2$workflows = Get-CrmRecords -EntityLogicalName workflow -FilterAttribute statecode -FilterOperator eq -FilterValue 1 # 1 = Active 3foreach ($wf in $workflows) { 4 Disable-CrmRecord -EntityLogicalName workflow -Id $wf.workflowid 5 Write-Host "Disabled Workflow: $($wf.name)" 6}
  • Verification: Attempt to manually create a record in the UI. If it triggers a 5-second loading spinner followed by an error, a process is still active.

13. Date Out of Range

  • Category: Data Format
  • Root Cause: Source data contains dates older than 1/1/1753 (SQL MinDate) or 1/1/1900 (Dataverse MinDate). This is common in legacy "Date of Birth" fields or placeholder dates.
  • Official Citation: DateTimeAttributeMetadata Class
  • Technical Resolution: Implement a transformation rule in your ETL: IF SourceDate < '1900-01-01' THEN SET TargetDate = NULL
  • Reproducible Artifact (C# Transformation):
1DateTime? safeDate = sourceDate; 2if (sourceDate.HasValue && sourceDate.Value.Year < 1900) 3{ 4 safeDate = null; // Or use new DateTime(1900, 1, 1); 5} 6entity["birthdate"] = safeDate;

14. Entity Reference Cannot Be Found

  • Category: Polymorphic Lookups
  • Root Cause: A lookup field (e.g., parentcustomerid) points to a GUID that does not exist in either the Account or Contact table. This often happens when the referenced record was deleted in the source but the relationship was not cascaded.
  • Technical Resolution: Do not fail the entire batch. Catch the FaultException, log the specific GUID as a "Broken Link," and retry the create operation without that specific lookup value.
  • Verification: Query the Failures table in your migration tool. If you see >10% failure rate, run a SQL query on the source to identify "Orphaned Records" before migrating.

15. Currency Mismatch (TransactionCurrencyId)

  • Category: Finance / Multi-Currency
  • Root Cause: You are migrating a record with a Money field (e.g., revenue), but the transactioncurrencyid lookup is null, or the referenced currency does not exist in the target.
  • Official Citation: Transaction Currency (Currencies) Entity
  • Technical Resolution:
    1. Migrate the transactioncurrency table first.
    2. Ensure the isocurrencycode (e.g., USD, EUR) matches exactly.
    3. If the source transactioncurrencyid is null, default it to the target organization's Base Currency.

16. String or Binary Data Would Be Truncated

  • Category: Data Schema
  • Root Cause: The text in the source column is longer than the MaxLength defined in the target Dataverse column (e.g., Source: 200 chars, Target: 100 chars).
  • Technical Resolution:
    • Fix A (Preferred): Go to the Power Apps Maker Portal, select the Column, and increase "Maximum Character Count."
    • Fix B (Data Loss): Truncate the string in your ETL script.
  • Reproducible Artifact (SQL Truncation):
1-- Safer to truncate than to fail 2SELECT LEFT([Description], 2000) as [Description], [Subject] 3FROM [Incident]

17. Entity State is Invalid

  • Category: Status / Lifecycle
  • Root Cause: Attempting to create a record directly in a "Resolved" or "Inactive" state (e.g., statecode=1). Some system entities do not allow creation in a closed state.
  • Official Citation: Update Entity State (SetStateRequest)
  • Technical Resolution: Two-Step Ingestion:
    1. Create the record in Active state (statecode=0).
    2. Send a separate UpdateRequest or SetStateRequest to close it.
  • Verification: Compare the count of Active vs Inactive records in Source vs Target.

18. Root Element is Missing (XML)

  • Category: Data Structure / Notes
  • Root Cause: You are migrating a Note (Annotation) that contains an attached file, but the documentbody (Base64 string) is corrupt, empty, or the mimetype is missing.
  • Technical Resolution: Validate that documentbody is not null and is a valid Base64 string. IF documentbody IS NOT NULL AND mimetype IS NULL SET mimetype = 'application/octet-stream'

19. Managed Property Error

  • Category: Solution Layering
  • Root Cause: You are trying to update a component (like a View, Form, or Attribute) that is part of a Managed Solution where IsCustomizable is set to False.
  • Official Citation: Managed Properties
  • Technical Resolution: You cannot directly modify these components via migration API. You must install a new version of the Managed Solution that contains the desired changes (e.g., longer field length).

20. Missing Dependency

  • Category: Solution Import
  • Root Cause: You are importing a Solution file (.zip) that references a component (e.g., a Global OptionSet or a 3rd Party DLL) that does not exist in the target environment.
  • Technical Resolution:
    1. Unzip the solution and open solution.xml.
    2. Search for the <MissingDependency> tag to identify the missing GUID/Name.
    3. Install the prerequisite solution first.
  • Verification: The import log will explicitly list the "Required Solution Name."

21. Attachment Limit Exceeded (Exchange Sync)

  • Category: Email Integration
  • Root Cause: A migrated email contains an attachment larger than 35 MB. While Dataverse allows files up to 128 MB, Exchange Online has a hard transmission limit of 35 MB (after Base64 encoding overhead).
  • Official Citation: Exchange Online Limits (Message Limits)
  • Technical Resolution: You cannot sync these emails to Outlook. Strategy: Detect attachments > 25 MB during migration. Strip the attachment, upload it to Azure Blob Storage, and append the Blob URL to the email body text.
  • Verification: Check the "Server-Side Synchronization" dashboard for "Permanent Failures" with error code ExchangeAttachmentLimit.

22. Workforce / Resource Constraint

  • Category: Field Service
  • Root Cause: Creating a BookableResource record fails because the associated User or Contact does not have a valid Calendar (Work Hours) defined in the target.
  • Official Citation: Set up bookable resources
  • Technical Resolution: Order of Operations:
    1. Migrate calendar and calendarrule entities.
    2. Migrate systemuser.
    3. Migrate bookableresource. Note: If the Calendar is missing, the scheduling engine cannot instantiate the resource.

23. Duplicate Lookup Reference

  • Category: Data Quality
  • Root Cause: Ambiguous resolution. You are mapping a lookup based on "Name" (e.g., parentcustomerid = "John Smith"), but two records named "John Smith" exist in the target.
  • Technical Resolution: Strict GUID Mapping: Never use text-based lookup resolution for migration. Always map Source GUID to Target GUID.
  • Reproducible Artifact (FetchXML Check):
1<fetch aggregate="true"> 2 <entity name="contact"> 3 <attribute name="fullname" alias="name_count" aggregate="count"/> 4 <attribute name="fullname" alias="name_group" groupby="true"/> 5 <order alias="name_count" descending="true"/> 6 </entity> 7</fetch>

24. Team Template Mismatch

  • Category: Access Teams
  • Root Cause: You are migrating records that use Access Teams (auto-created teams), but the teamtemplateid refers to a template that does not exist in the target environment.
  • Technical Resolution: Access Team Templates are not solution-aware. You must manually recreate them in the Target environment and map the Old Template ID to the New Template ID in your migration script.

25. Invalid Plugin Assembly (Security)

  • Category: Security / Sandbox
  • Root Cause: The assembly contains code forbidden in the Dynamics 365 Sandbox isolation mode (Partial Trust). Common blockers: System.IO (File access), System.Reflection, or direct SQL connections.
  • Official Citation: Plug-in isolation, trusts, and statistics
  • Technical Resolution: Refactor the code. Move heavy logic or prohibited namespaces to Azure Functions, which can be triggered by a Webhook or Service Bus.

26. Business Process Flow (BPF) Error

  • Category: Process Automation
  • Root Cause: The traversedpath field contains a comma-separated list of Stage GUIDs. If the BPF definition in the target has different Stage GUIDs than the source, the record will fail to load.
  • Official Citation: Business process flow entity serialization
  • Technical Resolution: Do not migrate the traversedpath string directly. You must create a "Stage Map" (Source Stage GUID -> Target Stage GUID) and reconstruct the path string during transformation.

27. PrincipalAccess Missing (POA Table)

  • Category: Sharing (Critical)
  • Root Cause: The PrincipalObjectAccess (POA) table governs "Share" and "Assign" rights. Migration fails if the PrincipalId (User) or ObjectId (Record) does not exist yet.
  • Official Citation: Use Record Sharing to Manage Access
  • Technical Resolution: The "Caboose" Strategy: Migration of the POA table must be the absolute last step of the entire project.
    • Step 1: Migrate Users & Teams.
    • Step 2: Migrate All Data.
    • Step 3: Migrate POA Table.
  • Reproducible Artifact (SDK Request):
1// Use GrantAccessRequest, not Create. 2// Direct SQL insert into POA is NOT supported in Online. 3var grantRequest = new GrantAccessRequest { 4 PrincipalAccess = new PrincipalAccess { 5 Principal = new EntityReference("systemuser", userId), 6 AccessMask = AccessRights.ReadAccess | AccessRights.WriteAccess 7 }, 8 Target = new EntityReference("account", accountId) 9}; 10service.Execute(grantRequest);

28. SQL Connection Closed

  • Category: Connectivity
  • Root Cause: Transient network failure between your On-Premise server and the Azure Data Center, causing the SqlConnection to drop.
  • Technical Resolution: Configure the connection string to handle transient failure.
  • Artifact (Connection String): Server=tcp:org.crm.dynamics.com,1433;Database=org;Authentication=ActiveDirectoryPassword;ConnectRetryCount=3;ConnectRetryInterval=10;

29. Web Resource Content Invalid

  • Category: UI / JavaScript
  • Root Cause: JavaScript files contain unsupported DOM manipulation methods like document.getElementById or $("#id"), which fail in the Unified Interface.
  • Technical Resolution: Run the Solution Checker. Replace all jQuery/DOM calls with the Client API (formContext.getControl()).

30. Portal/Site Setting Missing

  • Category: Power Pages (Portals)
  • Root Cause: Portal configuration (Adxstudio) relies on specific GUIDs for "Site Settings" and "Snippets." Standard data loaders often generate new GUIDs, breaking the portal linkage.
  • Technical Resolution: Use the Configuration Migration Tool (CMT) provided in the SDK. It supports "Schema validation" and maintains referential integrity specifically for Portal entities.

Section 2: Deep Dive – The "Migration Killers"

1. The "Sandbox Isolation" Trap (System.Security.SecurityException)

The Error: "Request for the permission of type 'System.Security.Permissions.SecurityPermission' failed." The Context: On-Premise environments allowed "Full Trust" plugins. Dynamics 365 Online enforces Sandbox Mode (Partial Trust). Why it happens: Your legacy code tries to access file paths, registry keys, or external network calls directly. The Fix:

  1. Audit: Run the "Custom Code Validation Tool" from XrmToolBox.
  2. Refactor: Move external calls to Azure Functions triggered by Webhooks.
  3. ClonePartner Approach: We bypass this by migrating data directly to the SQL layer via APIs, bypassing the plugin execution pipeline entirely during the initial load (BypassCustomPluginExecution option).

2. The Circular Reference Deadlock

The Error: "Record A requires Record B, but Record B requires Record A." The Context: Common in Account/Contact relationships (Primary Contact vs. Parent Account). The Fix:

  • Pass 1: Create Record A with the Lookup field empty.
  • Pass 2: Create Record B pointing to Record A.
  • Pass 3: Update Record A to point to Record B.
  • Tip: Using standard "Flat File" importers often fails here. You need a script that supports "Multi-Pass" ingestion.

3. The "2-Minute" SQL Timeout

The Error: SqlException: Timeout expired. The Context: Dynamics 365 Online has a hard limit on SQL execution time (usually 2 minutes) from the application layer. Heavy join queries in your migration source will kill the connection. The Fix:

  • Do not use OData for massive reads. Use TDS Endpoint (Preview) or fetch data in simplified batches.

Index: Ensure your SystemUserId, StateCode, and CreatedOn columns are indexed in the source SQL DB before export.

Frequently Asked Questions