If you’ve been working with plugins in Microsoft Dynamics 365 for a while, you’ve probably come across the IExecutionContext.Depth
property. This tells your plugin how deep in a call stack of plugins it is. A depth of 1 means it is being triggered by a direct user action, e.g. updating a record. If that plugin does something that triggers another plugin, that plugin will have a depth of 2, and so on.
Preventing Infinite Loops
Because plugins can do operations that trigger other plugins, there is a danger that poorly written code can end up in an infinite loop of one plugin calling another, which calls the first plugin again, which calls the second one, and so on until all your server resources are used and it dies, giving you some very unhappy users.
To prevent this, the Microsoft Dynamics 365 platform enforces a limit on the plugin depth. By default, this is set to 8. This means that when the 8th plugin is fired, the platform will kill that entire stack with the error message:
This workflow job was canceled because the workflow that started it included an infinite loop. Correct the workflow logic and try again. For information about workflow logic, see Help.
New features in v9
Version 9 of Microsoft Dynamics 365 for Sales brings many new features. At the same time, the underlying Power Platform has been separated from the 1st party apps such as Sales. Those new features are now being implemented in a similar way to 3rd party customizations, including using plugins.
For example, the new Action Card functionality uses plugins attached to the Create and Update messages on contacts.
Errors after upgrading
If you were close to the maximum plugin depth limit before, you might start seeing some of these “infinite loop” errors after upgrading. This isn’t because of any new problem in your code, but because Microsoft’s own plugins are using up some of the available plugin depth.
We had an example of this today – a plugin that synchronizes a hierarchy of entities coming from an ERP system to a deduplicated hierarchy of account and contact entities started failing. Turning on plugin trace logging gave us the answer:
System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: This workflow job was canceled because the workflow that started it included an infinite loop. Correct the workflow logic and try again. For information about workflow logic, see Help. (Fault Detail is equal to Exception details:
ErrorCode: 0x80044182
Message: This workflow job was canceled because the workflow that started it included an infinite loop. Correct the workflow logic and try again. For information about workflow logic, see Help.; [Microsoft.Dynamics.AppCommon.Plugins: Microsoft.Dynamics.AppCommon.Plugins.PostOperationHandleContactActionCard]
[7cb6ad1b-7e8d-4955-a2a9-694bef2c84dc: Create of acton card for contact]
Entered Microsoft.Dynamics.AppCommon.Plugins.PostOperationHandleContactActionCard.Execute(), Correlation Id: f44c7c54-f16d-408f-903e-e386f22aadc3, Initiating User: c1cab4c8-f553-432c-8430-10262d185543
Exception: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: This workflow job was canceled because the workflow that started it included an infinite loop. Correct the workflow logic and try again. For information about workflow logic, see Help. (Fault Detail is equal to Exception details:
ErrorCode: 0x80044182
Message: This workflow job was canceled bec...).
The error is coming from the new action card plugin that was introduced in v9. The other information in the plugin trace log lets us see a series of plugin executions that we would normally expect, getting up to depth 7, before this error is eventually triggered.
Fixing the problem
Luckily this solution is on-premise, so we have the option of increasing the maximum plugin depth as a quick fix:
PS> $set = Get-CrmSetting -SettingType WorkflowSettings
PS> $set.MaxDepth = 10
PS> Set-CrmSetting -Setting $set
This got us working again quickly, but isn’t an option when we look forward to moving to Online.
As a longer-term solution we’ll need to look at refactoring the plugin to do more of the work in each plugin step so it doesn’t need to recurse so much.
Looking to the future
This is a really good example of the “brave new world” for Microsoft Dynamics 365. A world in which the 1st party apps such as Sales are truly built as apps on the Power Platform with the same options available to other customizers and ISVs. One of the powerful features of CRM was always its extensibility model. With this shift in approach I think we can only see this extensibility story getting even more compelling. We just have to be mindful that, even in environments with no other outside customizations, we still have Microsoft “customizations” in the system as well.