Dataverse Plug-in Troubleshooting: Identify, Diagnose, and Resolve Issues Effectively

Table of Contents

This article provides comprehensive information about common errors that may arise during plug-in execution within Dataverse. It aims to equip you with the knowledge to effectively identify, diagnose, and resolve these issues, ensuring the smooth operation of your Dataverse environment. Understanding these potential pitfalls and their solutions is crucial for developers and administrators working with Dataverse plug-ins.

Error “Time conversion couldn’t be completed”

Time conversion error

Error Code: -2147220956
Error Message: The conversion could not be completed because the supplied DataTime did not have the Kind property set correctly. For example, when the Kind property is DateTimeKind.Local, the source time zone must be TimeZoneInfo.Local.

This error typically surfaces during a TimeZoneInfo.ConvertTimeToUtc(DateTime, TimeZoneInfo) call within your plug-in code. It specifically occurs when attempting to convert a DateTime value from time zones like Santiago or Volgograd to Coordinated Universal Time (UTC). The root cause lies in a known limitation within the product itself, and unfortunately, there is currently no available workaround to directly address this issue.

To gain a deeper understanding of time zone configurations within Dataverse and how they might relate to this error, refer to the documentation on Specify time zone settings for a user. While it may not provide a direct fix for the “Time conversion couldn’t be completed” error, understanding time zone settings can be beneficial in preventing other time-related issues in your plug-ins.

Error “Sandbox Worker process crashed”

Sandbox Worker process crashed

Error Code: -2147204723
Error Message: The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.

This error message indicates a critical failure: the worker process responsible for running your plug-in code has unexpectedly terminated. While the message points to a problem within the plug-in code as the typical cause, it’s important to note that the crash could also stem from other plug-ins running concurrently within the same organization. Due to the abrupt nature of the process crash, detailed information about the precise cause is often lost. However, post-mortem analysis of crash dumps reveals four primary culprits behind this error:

  • Unhandled exception in the plug-in
  • Using threads to queue work with no try/catch in thread delegate
  • Stack Overflow error in the plug-in
  • Worker process reaches the memory limit

Let’s delve into each of these causes to understand how they can lead to a “Sandbox Worker process crashed” error and how to mitigate them.

Unhandled exception in the plug-in

Unhandled exception

Robust plug-in development necessitates anticipating potential points of failure within your code. A crucial practice is to encapsulate operations that might fail within try-catch blocks. When an error is caught, instead of allowing the process to crash, you should gracefully terminate the operation by throwing an InvalidPluginExecutionException. This exception allows you to provide a user-friendly and informative error message, enhancing the overall user experience and facilitating debugging.

For comprehensive guidance on exception handling within plug-ins, consult the article Handle exceptions in plug-ins.

A frequent scenario where unhandled exceptions occur involves using methods like HttpClient.SendAsync or HttpClient.GetAsync. These methods, designed for asynchronous operations and returning a Task, can pose challenges in synchronous plug-in environments. Developers sometimes resort to using Task\.Result to force synchronous behavior. However, when errors arise, Result returns an AggregateException, which can bundle multiple failures into a single, complex exception, making error handling cumbersome.

A more effective approach is to use Task\.GetAwaiter().GetResult(). This method propagates the underlying, specific exception that caused the failure, simplifying error diagnosis and handling.

The following C# code example demonstrates the correct way to manage exceptions and handle outbound calls using HttpClient.GetAsync within a plug-in:

using Microsoft.Xrm.Sdk;
using System;
using System.Net.Http;

namespace ErrorRepro
{
    public class AsyncError : IPlugin
    {
        private readonly string webAddress;

        public AsyncError(string unsecureConfig)
        {
            if (string.IsNullOrEmpty(unsecureConfig)) {
                throw new InvalidPluginExecutionException("The ErrorRepro.AsyncError plug-in requires that a Url be set in the unsecure configuration for the step registration.");
            }
            webAddress = unsecureConfig;

        }

        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService =
            (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            tracingService.Trace($"Starting ErrorRepro.AsyncError");
            tracingService.Trace($"Sending web request to {webAddress}");

            try
            {
                string responseText = GetWebResponse(webAddress, tracingService);
                tracingService.Trace($"Result: {responseText.Substring(0, 100)}");
            }
            catch (Exception ex)
            {
                tracingService.Trace($"Error: ErrorRepro.AsyncError {ex.Message}");
                throw new InvalidPluginExecutionException(ex.Message);
            }
            tracingService.Trace($"Ending ErrorRepro.AsyncError");
        }

        //Gets the text response of an outbound web service call
        public string GetWebResponse(string webAddress, ITracingService tracingService)
        {
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    client.Timeout = TimeSpan.FromMilliseconds(15000); //15 seconds
                    client.DefaultRequestHeaders.ConnectionClose = true; //Set KeepAlive to false

                    HttpResponseMessage response = client.GetAsync(webAddress).GetAwaiter().GetResult(); //Make sure it is synchronous
                    response.EnsureSuccessStatusCode();

                    tracingService.Trace($"ErrorRepro.AsyncError.GetWebResponse succeeded.");

                    string responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); //Make sure it is synchronous

                    tracingService.Trace($"ErrorRepro.AsyncError.GetWebResponse responseContent parsed successfully.");

                    return responseContent;

                }
            }
            catch (Exception ex)
            {
                //Capture the inner exception message if it exists.
                // It should have a more specific detail.
                string innerExceptionMessage = string.Empty;
                if (ex.InnerException != null) {
                    innerExceptionMessage = ex.InnerException.Message;
                }

                tracingService.Trace($"Error in ErrorRepro.AsyncError : {ex.Message} InnerException: {innerExceptionMessage}");
                if (!string.IsNullOrEmpty(innerExceptionMessage))
                {
                    throw new Exception($"A call to an external web service failed. {innerExceptionMessage}", ex);
                }

                throw new Exception("A call to an external web service failed.", ex);
            }
        }
    }
}

This example showcases how to properly handle exceptions when making outbound web service calls from within a plug-in, ensuring robustness and preventing unhandled exceptions from crashing the worker process.

Using threads to queue work with no try/catch in thread delegate

Thread error

Employing parallel execution patterns within plug-ins is generally discouraged and considered an anti-pattern. As highlighted in the best practices article: Don’t use parallel execution within plug-ins and workflow activities, such patterns can introduce complexities in managing transactions within synchronous plug-ins. Beyond transaction management, another significant risk associated with using threads is that any unhandled exceptions within a thread delegate, outside of a try/catch block, can lead to worker process crashes.

Important Consideration: When a worker process crashes due to an unhandled exception in a thread, the execution of all plug-ins currently running within that process is terminated. This includes plug-ins that are not related to yours or maintained by you, potentially causing unexpected disruptions across the Dataverse environment.

Application Insights to the rescue

Historically, obtaining detailed debugging information, such as stack traces, for unhandled plug-in exceptions that caused worker process crashes was a significant challenge. However, Dataverse now offers seamless integration with Application Insights for logging execution failures. By linking Application Insights to your Dataverse environment, automatic logging of plug-in crashes is enabled. This integration dramatically improves troubleshooting capabilities.

For detailed instructions on setting up this integration, refer to Export data to Application Insights.

Once Application Insights is linked, a wealth of data related to worker process crashes becomes readily available for in-depth troubleshooting.

Steps to Navigate to Crash Reports in Application Insights:

  1. Link Application Insights to your environment: Follow the steps outlined in Link Application Insights to your environment.
  2. Reproduce the error: Trigger a plug-in exception that results in a worker process crash.
  3. Access Application Insights: In the Power Platform admin center, navigate to Application Insights.
  4. Navigate to Failures: In the Application Insights portal, select Failures in the left-hand navigation panel.
  5. Select Exceptions: On the Failures page, choose the Exceptions tab.
  6. Filter by Exception Problem ID: Under Exception Problem ID, in the Overall list, locate and select Microsoft.PowerPlatform.Dataverse.Plugin.PluginWorkerCrashException.
  7. Select PluginWorkerCrashException: On the right side of the page, under Overall, select PluginWorkerCrashException again. This will display a detailed view of all recorded worker process crash exceptions.
  8. Find and Select the Specific Exception: Browse the list of exceptions in the left panel and select the one you wish to investigate. The detailed exception report will appear on the right.
  9. Access Stack Trace: To view the stack trace, expand the CrashDetails section within the exception report.

By leveraging Application Insights, you gain unprecedented visibility into plug-in crashes, enabling faster and more effective resolution of these critical errors.

Stack overflow error in the plug-in

Stack overflow error

Stack Overflow errors in plug-ins often manifest immediately after code modifications. Developers who utilize base classes to streamline development should be particularly mindful of changes to these base classes, as plug-ins relying on them can be susceptible to stack overflow issues stemming from base class alterations.

The most common cause is uncontrolled recursion, where a function calls itself repeatedly without a proper termination condition. Even with a termination condition in place, if it doesn’t cover all possible scenarios, a stack overflow can still occur. For a deeper understanding of stack overflow exceptions, refer to StackOverflowException Class > Remarks.

When encountering this error, meticulously review recent code changes, not only within the plug-in itself but also in any dependent code, including base classes or utility libraries.

Example

Consider the following plug-in code, intentionally designed to induce a StackOverflowException through unbounded recursion. Despite attempts to use tracing and exception handling, neither tracing output nor the intended error message will be produced because the worker process terminates prematurely due to the stack overflow.

using Microsoft.Xrm.Sdk;
using System;

namespace ErrorRepro
{
    public class SOError : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService =
           (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            tracingService.Trace($"Starting ErrorRepro.SOError");

            try
            {
                tracingService.Trace($"Calling RecursiveMethodWithNoLimit to trigger StackOverflow error.");

                RecursiveMethodWithNoLimit(tracingService); //StackOverflowException occurs here.
            }
            catch (Exception ex)
            {
                //This trace will not be written
                tracingService.Trace($"Error in ErrorRepro.SOError {ex.Message}");

                //This error will never be thrown
                throw new InvalidPluginExecutionException($"Error in ErrorRepro.SOError. {ex.Message}");
            }

            //This trace will never be written
            tracingService.Trace($"Ending ErrorRepro.SOError");
        }

        public static void RecursiveMethodWithNoLimit(ITracingService tracingService)
        {
            tracingService.Trace($"Starting ErrorRepro.SOError.RecursiveMethodWithNoLimit");

            RecursiveMethodWithNoLimit(tracingService);

            tracingService.Trace($"Ending ErrorRepro.SOError.RecursiveMethodWithNoLimit");
        }
    }
}

In a synchronous plug-in step, executing the preceding code will result in the following error response in the Web API (when configured to include additional details with errors):

{
    "error": {
        "code": "0x8004418d",
        "message": "The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.\\r\\nSystem.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.\\r\\n   at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #8503641A",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionSourceKey": "Plugin/ErrorRepro.SOError, ErrorRepro, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c2bee3e550ec0851",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepKey": "d5958631-b87e-eb11-a812-000d3a4f50a7",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiDepthKey": "1",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiActivityIdKey": "a3028bda-73c2-4eef-bcb5-157c5a4c323e",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiPluginSolutionNameKey": "Active",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepSolutionNameKey": "Active",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionCategory": "SystemFailure",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionMesageName": "SandboxWorkerNotAvailable",
        "@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionHttpStatusCode": "500",
        "@Microsoft.PowerApps.CDS.HelpLink": "http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a8004418d&client=platform",
        "@Microsoft.PowerApps.CDS.TraceText": "\\r\\n[ErrorRepro: ErrorRepro.SOError]\\r\\n[d5958631-b87e-eb11-a812-000d3a4f50a7: ErrorRepro.SOError: Create of account]\\r\\n\\r\\n",
        "@Microsoft.PowerApps.CDS.InnerError.Message": "The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.\\r\\nSystem.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.\\r\\n   at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #8503641A"
    }
}

The plug-in tracelog will capture the error as follows:

Unhandled exception:
Exception type: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]
Message: The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.
System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.
   at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #4BC22433Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  <ActivityId>48c5818e-4912-42f0-b1b6-e3bbe7ae013d</ActivityId>
  <ErrorCode>-2147204723</ErrorCode>
  <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  <HelpLink i:nil="true" />
  <Message>The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.
System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.
   at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64&amp;amp; wcfExecInMs, Int64&amp;amp; initializeInMs, Int64&amp;amp; trackCallInMs, Int64&amp;amp; trackGoodReturnInMs, Int64&amp;amp; waitInMs, Int64&amp;amp; taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #4BC22433</Message>
  <Timestamp>2021-03-06T22:14:22.0629638Z</Timestamp>
  <ExceptionRetriable>false</ExceptionRetriable>
  <ExceptionSource>WorkerCommunication</ExceptionSource>
  <InnerFault i:nil="true" />
  <OriginalException>System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.

Server stack trace:
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64&amp;amp; wcfExecInMs, Int64&amp;amp; initializeInMs, Int64&amp;amp; trackCallInMs, Int64&amp;amp; trackGoodReturnInMs, Int64&amp;amp; waitInMs, Int64&amp;amp; taskStartDelay)</OriginalException>
  <TraceText i:nil="true" />
</OrganizationServiceFault>

Worker process reaches the memory limit

Memory limit error

Each worker process operates with a finite memory allocation. Scenarios involving multiple concurrent operations processing substantial amounts of data can potentially exhaust this memory, leading to worker process crashes.

RetrieveMultiple with File data

A common trigger for memory exhaustion is plug-in execution during RetrieveMultiple operations that include file data retrieval. For instance, retrieving emails with file attachments can be problematic. The volume of data returned by such queries is inherently unpredictable, as emails can have varying numbers and sizes of attachments.

When multiple similar requests execute concurrently, the aggregate memory demand can escalate rapidly. If this demand surpasses the worker process’s memory limit, a crash occurs. The key mitigation strategy is to limit RetrieveMultiple queries that fetch entities with related file attachments. Instead of retrieving everything at once, fetch records using RetrieveMultiple and then retrieve related files individually using separate Retrieve operations as needed. This approach distributes the memory load and reduces the risk of exceeding limits.

Memory Leaks

Less frequently, memory leaks within the plug-in code itself can contribute to memory exhaustion. This often arises when plug-ins are not designed as stateless, violating a fundamental best practice. Refer to Develop Plugin implementations as stateless for detailed guidance. Stateful plug-ins that persistently accumulate data in stateful properties, such as arrays, can eventually consume all available memory, leading to crashes.

Transaction errors

Transaction error

Two common error types pertain to transaction management within plug-ins:

Error Code: -2146893812
Error Message: ISV code reduced the open transaction count. Custom plug-ins should not catch exceptions from OrganizationService calls and continue processing.

Error Code: -2147220911
Error Message: There is no active transaction. This error is usually caused by custom plug-ins that ignore errors from service calls and continue processing.

Note: The first error message is a more recent addition and is designed to pinpoint the problematic plug-in immediately. The second error, while still relevant, can sometimes be triggered by issues in other plug-ins, particularly custom workflow activities.

Any data operation failure within a synchronous plug-in automatically terminates the transaction encompassing the entire operation. For a broader perspective on transaction management and scalability, consult Scalable Customization Design in Microsoft Dataverse.

A common anti-pattern is developers attempting to “swallow” errors by wrapping potentially failing data operations in try/catch blocks and continuing execution as if the error didn’t occur. While this might be acceptable in client applications, within a Dataverse plug-in context, any data operation failure necessitates rolling back the entire transaction. You cannot simply ignore the error. Always ensure you throw an InvalidPluginExecutionException to properly signal the failure and allow Dataverse to handle the transaction rollback.

Error “Sql error: Execution Timeout Expired”

SQL timeout error

Error Code: -2147204783
Error Message: Sql error: ‘Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.’

Cause

SQL timeout errors can arise from a multitude of factors. Three prominent causes are outlined below:

  • Blocking
  • Cascade operations
  • Indexes on new tables

Blocking

The most frequent cause of SQL timeout errors is blocking: the operation is waiting for database resources that are currently locked by another SQL transaction. This mechanism is in place to protect data integrity and prevent long-running requests from degrading performance for other users.

Blocking can be triggered by concurrent operations. Plug-in code that functions flawlessly in isolation within a test environment might still be susceptible to blocking in production environments where multiple users are simultaneously initiating the same logic.

Scalable customization design is paramount when writing plug-ins. Refer to Scalable Customization Design in Dataverse to learn best practices for building robust and scalable customizations.

Cascade operations

Certain actions within your plug-in, such as assigning or deleting records, can trigger cascade operations on related records. These cascading actions can acquire locks on related records, potentially blocking subsequent data operations within your plug-in and leading to SQL timeouts.

Carefully consider the potential impact of cascading operations on data operations within your plug-in. For more information on cascade behaviors, see Table relationship behavior.

Because cascade behaviors can be configured differently across Dataverse environments, reproducing timeout errors related to cascading operations can be challenging unless environments are identically configured.

Indexes on new tables

If your plug-in interacts with tables or columns that have been recently created, Azure SQL’s index management capabilities might require a few days to optimize indexes fully. During this initial period, performance might be slightly lower, potentially contributing to timeout errors.

Errors due to user privileges

User privilege error

In client applications, you can often disable UI commands that users lack permissions to execute. However, plug-ins operate differently. They lack this inherent UI-level permission enforcement. Your plug-in code might attempt automated actions that the user initiating the operation doesn’t have the necessary privileges to perform.

To address privilege-related errors, you can register the plug-in to execute within the security context of a user known to possess the required privileges. This is achieved by setting the Run in User’s Context property during plug-in registration. Alternatively, you can programmatically impersonate another user with sufficient privileges within your plug-in code. Consult these resources for more information:

Error when executing in the context of a disabled user

Disabled user error

Attempting to execute a plug-in in the context of a disabled user will result in the following error:

Error Message: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: The user with SystemUserId=<User-ID> in OrganizationContext=<Context> is disabled. Disabled users cannot access the system. Consider enabling this user. Also, users are disabled if they don’t have a license assigned to them.

To troubleshoot this, you can query for registered steps associated with the disabled user, along with related plug-in and SdkMessage details. Use the following HTTP request, replacing placeholders with actual values:

https://<env-url>/api/data/v9.2/sdkmessageprocessingsteps
?$filter=_impersonatinguserid_value eq '<disabled-userId-from-error>'&
$expand=plugintypeid($select=name,friendlyname,assemblyname;
$expand=pluginassemblyid($select=solutionid,name,isolationmode)),sdkmessageid($select=solutionid,name)&
$select=solutionid,name,stage,_impersonatinguserid_value,mode

Error “Message size exceeded when sending context to Sandbox”

Message size exceeded error

Error Code: -2147220970
Error Message: Message size exceeded when sending context to Sandbox. Message size: ### Mb

This error occurs when the message payload exceeds 116.85 MB and a plug-in is registered for that message. The error message will specify the exact size of the oversized payload.

This size limit is a safeguard to prevent resource contention and ensure fair resource allocation among users and applications. It provides protection against excessively large message payloads that could negatively impact the availability and performance of the Dataverse platform.

While 116.85 MB is a substantial limit, it’s still possible to encounter this error, particularly when retrieving records with numerous related records containing large binary files.

If you encounter this error, consider these mitigation strategies:

  1. Remove the Plug-in: If the plug-in is not essential for the operation causing the error, removing the plug-in registration for that specific message type will allow the operation to complete without the size restriction.
  2. Modify Client Code: If the error originates from a custom client application, refactor the code to avoid processing such large payloads in a single operation. Implement logic to retrieve data in smaller chunks or optimize data retrieval strategies.

Error “The given key wasn’t present in the dictionary”

Key not present error

Dataverse extensively utilizes classes derived from the abstract DataCollection\ class. These classes, essentially dictionary-like structures, represent collections of key-value pairs. Examples include IExecutionContext.InputParameters, which is a ParameterCollection derived from DataCollection\. You access values within these collections using their associated keys.

Error codes

This error arises when your code attempts to access a value using a key that does not exist within the collection. This is a runtime error, not a platform error. The specific error code reported within a plug-in depends on whether the exception was caught and handled.

If the developer correctly catches the exception and throws an InvalidPluginExecutionException (as recommended in Handle exceptions in plug-ins), the following error is returned:

Error Code: -2147220891
Error Message: ISV code aborted the operation.

However, if the exception is not properly caught, the more generic error below is often reported:

Error Code: -2147220956
Error Message: An unexpected error occurred from ISV code.

Note: “ISV” stands for Independent Software Vendor, a legacy term still used in some error messages.

Cause

This error frequently occurs during development due to simple typos or incorrect casing in key names. Key values within these Dataverse collections are case-sensitive.

At runtime, the error often results from developers incorrectly assuming that a particular key will always be present in the collection. For example, in a plug-in registered for the Update event of a table, the Entity.Attributes collection only contains attributes that have been modified in the update operation. Accessing an attribute key that wasn’t actually updated will trigger this “key not present” error.

Resolution

The solution is straightforward: always check if a key exists in the collection before attempting to access its value.

When working with table columns within an Entity, use the Entity.Contains(String) method to verify if a column (attribute) exists before attempting to retrieve its value.

// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
    serviceProvider.GetService(typeof(IPluginExecutionContext));

// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
    context.InputParameters["Target"] is Entity)
    {
    // Obtain the target entity from the input parameters.
    Entity entity = (Entity)context.InputParameters["Target"];

    //Check whether the name attribute exists.
    if(entity.Contains("name"))
    {
        string name = entity["name"];
    }
}

Some developers use Entity.GetAttributeValue\(String) to mitigate this error. This method returns the default value for the specified type if the attribute is not found. While this can be helpful when the default value is null, be cautious with value types like DateTime. For DateTime, GetAttributeValue\(String) will return 1/1/0001 00:00 if the attribute is missing, rather than null, which might lead to unexpected behavior if you’re expecting a null value.

Error “You can’t start a transaction with a different isolation level than is already set on the current transaction”

Transaction isolation level error

Error Code: -2147220989
Error Message: You cannot start a transaction with a different isolation level than is already set on the current transaction

Plug-ins are primarily intended for business logic implementation. Modifying the Dataverse data schema (metadata) within a synchronous plug-in is not supported. Schema modifications are typically longer-running operations and can cause metadata caching inconsistencies for applications. These operations are permissible in plug-in steps registered for asynchronous execution.

Known issue: Activity.RegardingObjectId name value not set with plug-in

Activity RegardingObjectId issue

A common symptom of this issue is that the Regarding field in an activity record displays (No Name) instead of the expected primary name attribute value of the related record.

Within plug-ins, you can set lookup attributes using EntityReference values. The EntityReference.Name property is technically optional. In most cases, you don’t need to explicitly set it when assigning a lookup attribute value, as Dataverse typically handles this automatically. You should generally set lookup values during the PreOperation stage of the event pipeline (see Event execution pipeline).

However, an exception to this rule exists when setting the ActivityPointer.RegardingObjectId lookup. All entities derived from ActivityPointer inherit this lookup, including standard activities like Appointment, Chat, Email, Fax, Letter, PhoneCall, RecurringAppointmentMaster, and any custom entities designated as activity types (see Activity tables).

If you set ActivityPointer.RegardingObjectId in the PreOperation stage, Dataverse does not automatically populate the Name property of the EntityReference. Consequently, the Name value remains null, and the formatted value that should display the related record’s name is missing when the activity record is retrieved, resulting in “(No Name)” in the UI.

Workaround

Two workarounds can address this issue:

  1. Set EntityReference.Name Explicitly: Before setting the ActivityPointer.RegardingObjectId attribute, explicitly set the EntityReference.Name property to the correct primary name field value of the related record.
  2. Set Lookup in PreValidation Stage: Instead of setting the lookup value in the PreOperation stage, set it during the PreValidation stage of the event pipeline. In the PreValidation stage, Dataverse correctly populates the Name property.

More information

By understanding these common Dataverse plug-in errors and their resolutions, you can significantly enhance your development and troubleshooting capabilities. Remember to leverage tracing, robust exception handling, and best practices for scalable customization to create reliable and efficient plug-ins.

If you have encountered other plug-in errors or have further insights into these issues, please share your experiences in the comments below! Your contributions can help the community collectively improve Dataverse plug-in development and troubleshooting.

Post a Comment