Dataverse plug-in exceptions explained
By Emil Björk · Microsoft business apps consultant, Gothenburg
The Dataverse plug-in errors that recur on every project — ISV code aborted (0x80040265), missing privilege (0x80040220), sandbox timeout, worker crash, infinite loop depth, key not present, assembly load — with fixes.
Plug-in errors surface in three places — the red dialog a user sees, the failed system job for an asynchronous step, and the Plug-in Trace Log — and the same handful of exceptions account for most of them. This reference lists them with the diagnostic path that finds the cause fastest. For what plug-ins are and how the pipeline works, start with Dataverse plug-ins explained and the plug-in execution pipeline.
First: find the real message
Every plug-in failure the user sees is wrapped. The dialog says something like "An error has occurred" with a Download Log File link; the log contains the exception chain, and the useful part is the innermost message and the plug-in type name. Get the log before doing anything else. For asynchronous steps, the System Jobs view (Settings > System Jobs, or the Power Platform admin centre) holds the same detail on the failed job.
Errors the platform raises around your code
"ISV code aborted the operation" — 0x80040265 (-2147220891)
Symptom. The generic message when a plug-in throws.
Cause. The plug-in threw an exception. If it threw InvalidPluginExecutionException, its message is shown to the user and this code is just the envelope. If it threw anything else — NullReferenceException, KeyNotFoundException, an HTTP exception — the platform wraps it and the user sees a .NET stack trace or an unhelpful summary.
Fix. Read the inner message. Then make the plug-in throw InvalidPluginExecutionException with a human message for every expected failure, and catch-and-rethrow unexpected ones with context: throw new InvalidPluginExecutionException($"Credit check failed for account {name}: {ex.Message}", ex);.
Prevention. A single try/catch around Execute that traces the full exception and rethrows as InvalidPluginExecutionException is the minimum every plug-in should have.
"Principal user (Id=…, type=8) is missing prvXxx privilege" / "SecLib::AccessCheckEx failed" — 0x80040220
Symptom. The plug-in works for admins and fails for ordinary users, or fails only in production.
Cause. Plug-ins run as the calling user unless the step is registered to run as a specific user. If the plug-in reads or writes a table the calling user cannot access — a configuration table, another business unit's records — the platform refuses with the missing privilege named in the message (prvReadnew_config, prvWriteaccount, and so on).
Fix. Either grant the privilege to the users' security role, or register the step with Run in User's Context set to a service account that has it, or use CreateOrganizationService(null) for the system account when elevated access is deliberate. Never solve it by giving users System Administrator.
Prevention. Decide per step whether it runs as the user or as the system; impersonation in plug-ins covers the trade-offs. Test with a real user role in every environment.
"The plug-in execution failed because the operation has timed-out at the Sandbox Client" / "… ran for more than the maximum allowed time"
Symptom. A synchronous plug-in fails after about two minutes; the user has been staring at a spinner.
Cause. The sandbox enforces a two-minute limit per plug-in execution. External HTTP calls to a slow endpoint, a RetrieveMultiple over a large table without paging, a loop that updates thousands of rows one at a time, or a lock wait on a hot record.
Fix. Move the work off the transaction: an asynchronous step, or a message to Service Bus consumed by an Azure Function — see the outbox pattern with Service Bus. For queries, add filters and paging; for bulk updates, use ExecuteMultiple or a batch job.
Prevention. No external HTTP call in a synchronous plug-in without a short timeout (seconds, not minutes) and a fallback; plug-ins vs Power Automate explains where the line is.
"The plug-in execution failed because no Sandbox Worker processes are currently available" / "Sandbox Worker process crashed"
Symptom. Intermittent failures across many plug-ins at once, often at busy times.
Cause. The sandbox host is under memory or CPU pressure — a plug-in with a memory leak (static collections that grow, undisposed HttpClient instances), unbounded recursion, or simply too many concurrent heavy executions.
Fix. Find the culprit through the Plug-in Trace Log timestamps and Application Insights; fix the leak. If load is genuine, spread it: asynchronous steps, batching, fewer steps per message.
Prevention. Make HttpClient static and shared, never store request data in static fields, and load-test synchronous plug-ins before go-live.
"This workflow job was canceled because the workflow that started it included an infinite loop. Correct the workflow logic and try again."
Symptom. An update fails after a pause, with a message about workflows even though no workflow exists.
Cause. Recursion. The plug-in's own Update fires the same step again (or a flow, or another plug-in, which updates the first record), and the platform cancels the chain when context.Depth passes 8. The message is shared with classic workflows.
Fix. In a pre-operation step, set values on context.InputParameters["Target"] instead of calling Update — the change rides the same transaction and does not re-trigger. In post-operation steps, check if (context.Depth > 1) return; when the plug-in should only act on the original user change, and use filtering attributes so the step only fires when relevant columns change.
Prevention. Register every step with filtering attributes, and document which plug-ins and flows write to which tables so loops are visible before they run.
Errors inside your code
"The given key was not present in the dictionary."
Symptom. KeyNotFoundException wrapped as 0x80040265.
Cause. entity["new_field"] or entity.GetAttributeValue on an attribute that is not in the Target — Update messages only carry the changed columns, and Create messages only the populated ones. Also context.InputParameters["Target"] on a message that has no Target (Delete carries an EntityReference), or a missing pre-image.
Fix. Use entity.Contains("new_field") and GetAttributeValue<T> (which returns default rather than throwing), and register a pre-image with the columns the plug-in needs to read.
Prevention. Never assume the Target has a column; treat the pre-image as the source of truth for unchanged values.
"Object reference not set to an instance of an object."
Symptom. NullReferenceException.
Cause. A lookup that returned null (GetAttributeValue<EntityReference> on an empty lookup), a pre-image not registered on the step, a Retrieve with a column set that did not include the column, or an OrganizationServiceContext used after disposal.
Fix. Null-check every attribute read; verify the step's image registration matches what the code expects; retrieve with explicit column sets that include everything used.
Prevention. A small helper that reads attributes from Target-then-PreImage-then-default removes most of these.
"Could not load file or assembly 'X' or one of its dependencies."
Symptom. The plug-in fails immediately, before any trace line.
Cause. The assembly references a NuGet package (Newtonsoft.Json, a client SDK) that was not deployed with it. Registered as a single assembly, plug-ins cannot load dependencies from disk.
Fix. Deploy as a plug-in package (the NuGet-based packaging supported since 2022, via pac plugin push or the Plug-in Registration Tool's Register New Package), which carries dependent assemblies. The older answer was ILMerge; it still works but packages are the supported path.
Prevention. Use plug-in packages from the start and pin dependency versions.
"Sql error: Generic SQL error" / deadlock / "Sql timeout expired" — 0x80044150
Symptom. Intermittent failures on writes, more often under load.
Cause. A SQL deadlock or lock timeout — two plug-ins updating the same rows in different orders, a long transaction holding locks while waiting on an external call, or a synchronous plug-in updating a parent record that many child operations also touch.
Fix. Retry once on this specific error in callers that can; shorten the transaction; remove external calls from it; touch parent records last.
Prevention. Consistent lock ordering across plug-ins and shorter synchronous steps.
Registration and deployment errors
"Plug-in assembly does not contain the required types" / "The plug-in type X is not registered"
Cause. The class was renamed or moved namespace, and the registration still points at the old type name; or the class is not public, does not implement IPlugin, or was excluded from the build.
Fix. Update the assembly in the registration tool (it re-reads types) and re-register steps for renamed classes.
"Unable to register assembly: version has changed" / update fails with dependent steps
Cause. The assembly's AssemblyVersion changed — Dataverse treats major/minor version changes as a new assembly and refuses to update in place while steps exist.
Fix. Keep AssemblyVersion fixed (use AssemblyFileVersion for build numbers), or unregister and re-register steps as part of the deployment.
Prevention. Deploy through solutions with the assembly and steps together, and never bump AssemblyVersion for a release.
The diagnostic order that works
- Get the exact inner message from the log file or the failed system job.
- Check the Plug-in Trace Log for the plug-in's own trace lines around that timestamp.
- Reproduce with the profiler and replay in Visual Studio with the real context.
- If it is intermittent, correlate with Application Insights and the other plug-ins and flows on the same table.
Dataverse tracing and logging sets up steps 2 and 4; the Plug-in Registration Tool deep dive covers step 3. The API-level errors a plug-in might receive from its own service calls — privilege, duplicate, throttling — are in Dataverse Web API errors.
Frequently asked questions
What does 'ISV code aborted the operation' (0x80040265) mean?
- A plug-in threw an exception and the platform rolled back the operation. It is the generic wrapper; the real message is the InvalidPluginExecutionException text the plug-in threw — or, if the plug-in threw any other exception type, the .NET message buried inside the error details. Throw InvalidPluginExecutionException with a clear message so users see the cause, not the wrapper.
Why does my plug-in time out after two minutes?
- Plug-ins run in the sandbox with a hard two-minute execution limit per step. Anything that can approach it — external HTTP calls, large RetrieveMultiple loops, bulk updates — belongs in an asynchronous step, an Azure Function behind a Service Bus queue, or a batch process, not inside the user's transaction.
What causes 'This workflow job was canceled because the workflow that started it included an infinite loop'?
- Recursion. A plug-in on Update updates the same record (or another record with a plug-in that updates the first), each update fires the plug-in again, and the platform kills the chain when context.Depth exceeds 8. Check context.Depth at the top of Execute and exit early, and use pre-operation steps that modify the Target instead of a second Update call.
How do I see what my plug-in actually did?
- Register a plug-in profiler session in the Plug-in Registration Tool and replay the captured context in Visual Studio, or enable the Plug-in Trace Log (Settings > Administration > System Settings) and write ITracingService.Trace lines — they appear in the trace log even when the plug-in succeeds, if the setting is set to All.
Further reading
Related guides
- Dataverse solution import errorsWhy Dataverse solution imports fail — missing dependencies, managed cannot overwrite unmanaged, version lower than installed, language not installed, connection reference and environment variable prompts, invalid flows, solution checker blocks.
- Dataverse Web API errors explainedDataverse Web API errors by code — 0x80040217 does not exist, 0x80040220 privilege, 0x80040333 duplicate, 0x80040237 duplicate key, 0x80048d19 payload, 0x80072322 throttling, @odata.bind mistakes — with fixes.
- The Dataverse plug-in execution pipelineHow Dataverse executes plug-ins — stages, pre/post images, transactional scope, and how multiple plug-ins interact on a single operation.
- AL compiler errors in Business CentralThe AL compiler errors every Business Central developer hits — AL0118, AL0132, AL0185, AL0296, AL0432, AL0603, AL0604, ID-range and symbol errors — with cause, fix, and prevention.
- AL runtime errors in Business CentralThe Business Central runtime errors AL developers and admins meet most — record already exists, does not exist, modified by another user, string length, locks and deadlocks, G/L inconsistency — with fixes.
Browse every guide in Customer Engagement or just Troubleshooting.
Spot something wrong or want a topic covered? Send a correction or a topic request — both are welcome.