I’ve written a fair few apps to integrate with Microsoft Dynamics 365 over the years, and you can guarantee at some point you’re going to hit an error that’s triggered by a plugin when you update a record. How do you find the true cause of the error and start debugging it?

If your app logs exception details you’ll get a starting point from the normal exception message:

try
{
  org.Update(entity);
}
catch (Exception ex)
{
  Console.WriteLine(ex.Message);
}

but if you’re just logging standard exception details you’re leaving a lot of helpful information on the table.

Which plugin?

The first step to debugging a plugin is to figure out which plugin is causing the problem. If it’s not obvious from the error text, where do you go next? You may be tempted to reach for plugin trace logs to find it. But you can actually get this information straight from the exception!

try
{
  org.Update(entity);
}
catch (FaultException<OrganizationServiceFault> ex)
{
  Console.WriteLine(ex.Message);

  if (ex.Fault.ErrorDetails.TryGetValue("Plugin.ExceptionFromPluginExecute", out var plugin))
    Console.WriteLine("Error is from plugin: " + plugin);
}

Trace log

Now we know which plugin caused the error it’s time to use a tool like the awesome Plugin Trace Viewer to get the logs from it, right?

Actually we can also pull the trace log from the failing plugin from the exception as well. This gives us even more information right at our fingertips within our application logs.

try
{
  org.Update(entity);
}
catch (FaultException<OrganizationServiceFault> ex)
{
  Console.WriteLine(ex.Message);

  if (ex.Fault.ErrorDetails.TryGetValue("Plugin.ExceptionFromPluginExecute", out var plugin))
    Console.WriteLine("Error is from plugin: " + plugin);

  if (!String.IsNullOrEmpty(ex.Fault.TraceText))
    Console.WriteLine("Trace log: " + ex.Fault.TraceText);
}

More details

If you look through the ex.Fault.ErrorDetails collection you’ll see more information which shows things like whether the error was caused by a plugin or the core Dataverse/Dynamics 365 system and whether you should retry the operation.

Web API

The examples I’ve shown so far are for apps using the SDK to integrate with Dataverse. What about if you’re using the Web API, maybe from some custom JavaScript? Thankfully all this extra information is available there too.

const response = await fetch('/api/data/v9.0/contacts(guid)', {
    method: 'PATCH',
    headers: {
      'Content-Type': 'application/json',
      'Prefer': 'odata.include-annotations="*"'
    },
    body: JSON.stringify({
      firstname: 'Mark',
      lastname: 'Carrington'
    })
  });

if (!response.ok) {
  const fault = response.json();

  console.log(fault.error.message);

  if (fault.error['@Microsoft.PowerApps.CDS.ErrorDetails.Plugin.ExceptionFromPluginExecute'])
    console.log(fault.error['@Microsoft.PowerApps.CDS.ErrorDetails.Plugin.ExceptionFromPluginExecute']);

  if (fault.error['@Microsoft.PowerApps.CDS.TraceText'])
    console.log(fault.error['@Microsoft.PowerApps.CDS.TraceText']);
}

There’s some more information on how the error appears for an error which causes the entire plugin process to crash in the Microsoft documentation on troubleshooting plugins.

However, one thing I didn’t find immediately obvious was that to get these details you need to request OData annotations via the Prefer header in the request – many thanks to Ben Gribaudo for his post on this!

2 thoughts on “MSDyn365 Internals: Plugin Debugging from SDK and Web API apps”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.