Troubleshooting Microsoft Dynamics 365 Data Migration: Resolving Lookup Failures, API Throttling, and Configuration Mismatches
Fix Microsoft Dynamics 365 data migration and configuration import errors. Learn to resolve missing lookup references, API throttling (429), and SQL timeouts.
- Root cause 1: Missing relational prerequisites (e.g., Business Units, Currencies, Teams) causing lookup reference resolution failures during bulk insert.
- Root cause 2: Exceeding Dataverse Service Protection API limits, resulting in HTTP 429 Too Many Requests and intermittent SqlTimeoutExceptions (0x80040216).
- Root cause 3: Misconfigured Data Maps in the Configuration Migration Tool (CMT) leading to GUID mismatches between source and target environments.
- Quick fix summary: Sequence imports hierarchically (Configuration -> Master -> Transactional), implement transient fault handling (retry logic) in your ETL, and use the Configuration Migration Tool exclusively for schema-exact configuration data.
| Migration Method | Best Use Case | Troubleshooting Complexity | Risk Level |
|---|---|---|---|
| Configuration Migration Tool (CMT) | Environment configuration data & reference data (e.g., Countries, Currencies) | Low (UI-driven XML log files) | Low |
| SSIS with KingswaySoft/PragmaticWorks | High-volume transactional data & complex ETL transformations | High (Requires SSIS debugging and Dataverse API tracing) | Medium |
| Azure Data Factory (ADF) | Enterprise-scale ELT pipelines and continuous syncing | Medium (Azure Monitor & Log Analytics) | Medium |
| Power Platform Dataflows | Self-service, simple flat-file migrations and one-off loads | Low (Basic refresh history and error counts) | Low |
Understanding Dynamics 365 Migration Architecture
When executing a Microsoft Dynamics 365 data migration, you are essentially interacting with the underlying Dataverse API architecture. Unlike direct SQL database migrations, Dataverse abstracts the database layer, meaning every Create, Update, or Upsert operation must pass through the OData Web API or the Organization Service SOAP endpoints. This architectural abstraction introduces unique failure domains: API throttling, plugin execution limits, and strict relational integrity checks.
Whether you are using custom C# scripts, SSIS, or the native Configuration Migration Tool to move Microsoft Dynamics 365 configuration data, the target environment enforces strict business logic. Migrating configuration data (like routing rules, queues, or custom reference tables) must be done before transactional data (like Accounts, Contacts, or Opportunities).
Error 1: Lookup Reference Resolution Failures
Symptom:
During an import, records fail with variations of the following error:
System.ServiceModel.FaultException\<OrganizationServiceFault>: The lookup reference could not be resolved. Entity: account, Attribute: primarycontactid, Value: 8a4b3c2d-1e2f-3g4h-5i6j-7k8l9m0n1o2p.
Root Cause: Dataverse requires that foreign key constraints (Lookups) point to a valid GUID that already exists in the target environment. If you attempt to migrate Accounts with a Primary Contact defined, but the Contacts have not yet been migrated (or were migrated with new GUIDs instead of their source GUIDs), the platform will reject the record.
Diagnostic Steps:
- Identify the failing entity and attribute from the log.
- Query the target environment using the Dataverse Web API to verify if the GUID exists.
- Check your migration tool's mapping configuration. If you are not maintaining source GUIDs (using the
idfield explicitly), relationships will inherently break.
Remediation:
- Multi-pass Migration: Execute a "two-pass" migration. Pass 1: Migrate all Accounts with lookup fields set to null. Migrate all Contacts. Pass 2: Update the Accounts with the correct
primarycontactid. - Maintain GUIDs: Always map the source Primary Key (e.g.,
accountid) to the target Primary Key. This is especially critical for Microsoft Dynamics 365 configuration data where internal GUIDs are heavily relied upon by workflows and plugins.
Error 2: Service Protection API Limits (Throttling)
Symptom:
Your high-speed migration tool suddenly drops throughput, and logs fill with HTTP 429 errors or:
Error Code: -2147015902. Message: Number of requests exceeded the limit of 6000 over time window of 300 Seconds.
Root Cause: Microsoft enforces Service Protection API limits to ensure system stability. These limits cap the number of concurrent requests, the number of requests per 5-minute sliding window, and the total execution time. Aggressive parallel SSIS or custom API loops will trigger these limits.
Diagnostic Steps:
- Inspect the HTTP response headers. Dataverse returns a
Retry-Afterheader when throttled. - Review Power Platform Admin Center > Analytics > Dataverse > API Statistics.
- Check if synchronous plugins are firing on your migrated entities. Plugins drastically increase execution time, hitting the compute limits faster.
Remediation:
- Implement Retry Logic: Ensure your middleware respects the
Retry-Afterheader. If using C#, utilize theTransientFaultHandlingapplication block or Polly to handle HTTP 429s gracefully. - Bypass Custom Business Logic: Temporarily disable synchronous plugins, classic workflows, and Power Automate flows triggered on
Createduring the initial data load. - Optimize Batching: If using
ExecuteMultipleRequest, keep batch sizes between 50 and 100. Larger batches often result inSqlTimeoutException(0x80040216) because the entire batch is wrapped in a single database transaction.
Error 3: SqlTimeoutException and Unexpected Errors (0x80040216)
Symptom:
Unhandled exception: System.ServiceModel.FaultException\<OrganizationServiceFault>: Generic SQL error. Error code: 0x80040216.
Root Cause: This generic error usually masks a SQL Server timeout on the backend. It occurs when a transaction takes longer than the platform's execution threshold (typically 2 minutes). This is common when importing records into tables with massive cascading relationships, or when index fragmentation in the target Dataverse instance is high.
Diagnostic Steps:
- Reduce the thread count or batch size of the migration task to 1.
- Attempt to migrate the failing record manually via the UI. If it fails in the UI, it's a data logic issue (e.g., cyclical relationships). If it succeeds, it's a load/concurrency issue.
- Check for SQL locks. Are you updating the same parent record simultaneously across multiple parallel threads? (e.g., associating 10,000 Contacts to a single Account concurrently causes lock contention on the Account record).
Remediation:
- Sort your source data by the parent lookup ID before migrating. This minimizes database deadlocks.
- If using the Configuration Migration Tool, ensure the schema file (
.xml) is correctly generated and does not include unnecessary auditing or history tables.
Best Practices for Microsoft Dynamics 365 Configuration Migration
When migrating schema-dependent configuration data (like portals, unified routing, or complex SLA setups), the Configuration Migration Tool (CMT) is the gold standard.
- Always generate a fresh schema file from the source environment.
- Exclude system-generated fields (CreatedOn, ModifiedBy) to prevent impersonation errors unless explicitly required.
- Pre-validate target environments for missing solution dependencies. Data migration will fail if the underlying entity schema (custom fields) has not been deployed via Solutions first.
Frequently Asked Questions
#!/bin/bash
# DevOps/SRE Diagnostic Script: Query Dataverse Web API for Failed System Jobs
# Useful for diagnosing asynchronous migration pipeline failures
# Configuration Variables
ENVIRONMENT_URL="https://your-env.crm.dynamics.com"
TENANT_ID="your-tenant-id"
CLIENT_ID="your-service-principal-client-id"
CLIENT_SECRET="your-client-secret"
# 1. Obtain Entra ID (Azure AD) Access Token
echo "Fetching Access Token..."
TOKEN_RESPONSE=$(curl -s -X POST https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "scope=$ENVIRONMENT_URL/.default" \
-d "grant_type=client_credentials")
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')
if [ "$ACCESS_TOKEN" == "null" ]; then
echo "Failed to authenticate. Check Service Principal credentials."
exit 1
fi
# 2. Query Dataverse for Failed Data Import Jobs (AsyncOperations)
# Filter by operationtype = 9 (Data Import) and statecode = 3 (Completed) and statuscode = 31 (Failed)
echo "\nQuerying for failed Data Import operations in the last 24 hours..."
curl -s -X GET "$ENVIRONMENT_URL/api/data/v9.2/asyncoperations?\$select=name,message,createdon,friendlymessage&\$filter=operationtype eq 9 and statuscode eq 31 and createdon ge Microsoft.Dynamics.CRM.LastXHours(PropertyValue=24)" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "OData-MaxVersion: 4.0" \
-H "OData-Version: 4.0" \
-H "Accept: application/json" | jq '.value[] | {JobName: .name, FailedOn: .createdon, Error: .friendlymessage}'
# 3. Check API Telemetry for Throttling (429) Indicators
# Note: Actual 429 telemetry is best viewed in Azure App Insights,
# but you can check plugin trace logs for timeout indicators.
echo "\nQuerying Plugin Trace Logs for generic SQL errors (0x80040216) or Timeouts..."
curl -s -X GET "$ENVIRONMENT_URL/api/data/v9.2/plugintracelogs?\$select=typename,messagename,exceptiondetails&\$filter=contains(exceptiondetails, '0x80040216') or contains(exceptiondetails, 'Timeout')" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "OData-MaxVersion: 4.0" \
-H "OData-Version: 4.0" \
-H "Accept: application/json" | jq '.value[] | {Plugin: .typename, Message: .messagename, Exception: .exceptiondetails | .[0:150]}'
Error Medic Editorial
Error Medic Editorial is composed of Senior Site Reliability Engineers and DevOps architects specializing in enterprise cloud platforms, Microsoft Dynamics 365, and large-scale data migrations.
Sources
- https://learn.microsoft.com/en-us/power-platform/admin/manage-configuration-data
- https://learn.microsoft.com/en-us/power-apps/developer/data-platform/api-limits
- https://learn.microsoft.com/en-us/power-apps/developer/data-platform/best-practices/work-with-metadata
- https://github.com/microsoft/PowerApps-Samples