Blog 3: Handling Transaction Errors in Dataverse Plug-ins

Dataverse plug-ins often operate within transactions, ensuring that database operations are either fully completed or entirely rolled back in case of failure. However, improper error handling in plug-ins can cause transaction failures, leading to data inconsistency and unexpected behavior.

In this blog, we will explore:
Common transaction errors in Dataverse plug-ins
How to prevent and debug transaction failures
Best practices for handling transactions effectively


⚠️ Understanding Transaction Errors in Plug-ins

Transaction errors occur when a plug-in operation disrupts the execution of an active database transaction.

🚨 Common Transaction Errors

Error CodeError Message
-2146893812ISV code reduced the open transaction count. Custom plug-ins should not catch exceptions from OrganizationService calls and continue processing.
-2147220911There is no active transaction. This error is usually caused by custom plug-ins that ignore errors from service calls and continue processing.

🔍 Why Do Transaction Errors Occur?

Transaction errors occur due to improper handling of service calls inside plug-ins. Below are the most common reasons:

1️⃣ Catching and Swallowing Exceptions in a Plug-in

Dataverse automatically rolls back transactions when a synchronous plug-in fails. However, catching exceptions without throwing them properly can break the transaction flow, leading to errors.

Bad Practice: Catching exceptions but not throwing InvalidPluginExecutionException

try
{
    service.Update(entity);
}
catch (Exception ex)
{
    tracingService.Trace($"Error: {ex.Message}");  // Error logged, but transaction continues incorrectly
}

Solution: Always throw InvalidPluginExecutionException

try
{
    service.Update(entity);
}
catch (Exception ex)
{
    tracingService.Trace($"Error: {ex.Message}");
    throw new InvalidPluginExecutionException("Transaction failed, rolling back.", ex);
}

💡 Why? This ensures the transaction is rolled back correctly.


2️⃣ Ignoring Errors from Service Calls

Another mistake is ignoring errors from service calls and allowing plug-in execution to continue.

Bad Practice: Continuing execution after a failed service call

try
{
    service.Delete(entityReference);
}
catch (Exception ex)
{
    tracingService.Trace("Error occurred, but continuing execution.");
    // Execution continues despite the failure
}

Solution: Stop execution immediately

try
{
    service.Delete(entityReference);
}
catch (Exception ex)
{
    tracingService.Trace($"Delete failed: {ex.Message}");
    throw new InvalidPluginExecutionException("Deletion failed, rolling back transaction.", ex);
}

💡 Why? Continuing execution after a failed operation breaks transaction integrity.


3️⃣ Performing Multiple Write Operations Without Handling Failures

Plug-ins often perform multiple create/update/delete operations. If one operation fails and is not properly handled, the entire transaction can become unstable.

Bad Practice: Executing multiple operations without error handling

service.Create(entity1);
service.Update(entity2);
service.Delete(entity3);

Solution: Wrap all operations in a try-catch block

try
{
    service.Create(entity1);
    service.Update(entity2);
    service.Delete(entity3);
}
catch (Exception ex)
{
    tracingService.Trace($"Transaction failed: {ex.Message}");
    throw new InvalidPluginExecutionException("Rolling back due to error.", ex);
}

💡 Why? Ensures all operations fail together rather than leaving partial updates.


🛠️ Debugging Transaction Errors

When a transaction error occurs, use the following debugging techniques:

✅ 1. Enable Tracing for Plug-ins

Dataverse tracing service can log transaction failures:

tracingService.Trace("Starting transaction...");

📌 View logs in: Power Platform Admin Center > Plugin Trace Logs


✅ 2. Use Application Insights for Advanced Debugging

If tracing is not enough, Application Insights can log transaction failures.

📌 Steps:

1️⃣ Enable Application Insights in your environment
2️⃣ Navigate to Failures > Exceptions in Application Insights
3️⃣ Look for TransactionFailureException

🔹 Sample Application Insights log:

{
    "error": {
        "code": "0x8004418d",
        "message": "Transaction aborted due to an unhandled exception."
    },
    "plugin": {
        "name": "MyCompany.PlugIn",
        "step": "Update Account",
        "solution": "Active"
    }
}

🎯 Best Practices for Handling Transactions in Plug-ins

✔️ Always throw InvalidPluginExecutionException on failure
✔️ Never catch exceptions without rethrowing them
✔️ Ensure all service calls are inside try-catch blocks
✔️ Log detailed error messages using ITracingService
✔️ Use Application Insights for advanced debugging


🔜 Coming Up Next…

In our next blog, we will cover “SQL Timeout Errors in Dataverse Plug-ins”, including how to diagnose and optimize queries for better performance.

4 thoughts on “Blog 3: Handling Transaction Errors in Dataverse Plug-ins

  1. Pro tip!

    Don’t go to Power Platform Admin Center > Plugin Trace Logs to read trace logs, it’s really hard to actually find the info you need.

    Instead, use the tool Plugin Trace Viewer in the XrmToolBox. Easier to find everything.

    Like

    1. Indeed, that was a great tool from your side, I really liked the live refresh and the set of features it offers. It also seems like the Dataverse Accelerator may have taken some inspiration from your idea and brought in a few key enhancements of their own. I recently explored the Plugin Monitoring feature it’s still in preview and a bit buggy, but definitely a helpful addition to the toolkit.

      Like

  2. There is no necessary to manually add try catch block for multiple operations, they are in plugin transaction and will fail/success together naturally.

    Even if they are in non-transaction area like pre-validation or even outside plugin, the try catch block will do nothing to make those operations consistent, it will not rollback or commit.

    Like

    1. Thanks for your insight, You are absolutely right that within the context of a synchronous plugin, operations executed using IOrganizationService are wrapped in the same platform-managed transaction so they will either all succeed or all fail together, ensuring consistency.

      That said, the emphasis on try catch in the blog is less about enforcing transaction boundaries and more about ensuring that errors are captured meaningfully. Without proper exception handling, a plugin crash may not log helpful diagnostic details (especially in pre production environments), and users might get generic error messages. So, while the platform manages transactional integrity, try catch still plays a valuable role in observability and maintainability. Appreciate you raising this point!

      Like

Leave a comment