Handling Exceptions in Plugins
The Problem
When trying to catch an exception in a plugin, you get this error:
ISV code reduced the open transaction count. Custom plug-ins should not catch exceptions from OrganizationService calls and continue processing.
Microsoft provides information about handling exceptions on plugins:
Handle exceptions in plug-ins
Use InvalidPluginExecutionException in plug-ins and workflow activities
Troubleshoot plug-ins
The last of these states, “You can’t swallow the error, so you must make sure to always return an InvalidPluginExecutionException.” This isn’t entirely true.
The Helpful Bit
The documentation often differentiates between synchronous and async plugins, for good reason. Synchronous plugins surface errors to the user, Ideally after being caught and re-thrown as an InvalidPluginExecutionException. They also participate in a transaction and can roll back the entire operation. Async operations don’t do either of these things. The document does briefly mention synchronous operation, but it doesn’t clarify error handling for async operations, or point out that in some cases your async plugin may actually be running synchronously.
On Async plugins, you can catch exceptions and continue processing. Here’s an example:
try
{
ExecuteTransactionResponse response =
(ExecuteTransactionResponse)service.Execute(exMultReq);
}
catch (Exception ex)
{
errored = true;
if (context.Mode == 0) //0 sync, 1 Async.
throw new InvalidPluginExecutionException(
$"Execute Multiple Transaction
Failed.\n{ex.Message}", ex);
}
if(errored == true)
{
//Do more stuff to handle it, such as Log the failure.
}
In this scenario, I am sending an ExecuteTransaction request, despite the variable name ‘exMultReq,’ as I want an all-or-nothing update of multiple records. If it fails, I log the result for the user to see, but don’t wish to run synchronously due to performance considerations.
The IExecutionContext.Mode enumeration in the OptionSets.cs file clarifies the mode of execution. If your plugin is registered as async, you may think checking the execution mode in the catch block is not necessary. But, you might change the registration and forget, or it may be indirectly triggered to run synchronously (IE operations triggered by an async plugin run synchronously). It is best to check the excution mode.
As far as I know, there is no way to catch an exception on a synchronous plugin and continue processing. Follow MSFT’s recommendation and re-throw an InvalidPluginExecutionException for anything running synchrously. If anyone knows of a way to do otherwise, please comment.
Disclaimer: Given the statement in MSFT’s documentation, this approach may not be supported. I’ve never had any issues with it, and believe it is just an oversight in the documentation. Use it at your own risk.
Conclusion
You can catch exceptions in asynchronous plugins and continue processing. Just be sure to verify the execution mode in case your code happens to be running synchronously, and throw an InvalidPluginExecutionException if it is.
Reference Material
MSFT: Handle exceptions in plug-ins
MSFT: Use InvalidPluginExecutionException in plug-ins and workflow activities
MSFT: Troubleshoot plug-ins
Stack Overflow: How to Safely Ignore an Error in Dynamics CRM Plugin?
CRM Answers: Custom plug-ins should not catch exceptions