How a plugin fails — and how easily you can figure out why it failed — matters as much as its happy-path logic.

InvalidPluginExecutionException

This is the one exception type Dataverse treats specially: throwing it rolls back the entire transaction (nothing saves) and shows its message text directly to the user in a clean error dialog, instead of a generic, unhelpful platform error.

throw new InvalidPluginExecutionException("A Case cannot be closed while it still has open tasks.");

Any other exception type still aborts the operation, but surfaces as a much less friendly generic error, often without your actual message visible to the end user — so for any deliberate, expected validation failure, always throw InvalidPluginExecutionException specifically.

Tracing

The ITracingService writes diagnostic messages that get stored with the plugin’s execution trace log — the primary tool for understanding what actually happened inside a plugin run, especially in production where you can’t attach a debugger.

var tracingService = (ITracingService)serviceProvider
    .GetService(typeof(ITracingService));

tracingService.Trace("Checking open tasks for Case {0}", target.Id);
Trace logging has to be turned on. By default, Dataverse only persists plugin trace logs for exceptions, or when trace logging is explicitly enabled at the environment level (Settings → Plugin Trace Log settings), or temporarily via a specific plugin step’s own “trace” setting. Writing tracingService.Trace(...) calls that are never actually persisted anywhere is a common early confusion — check the environment’s trace log settings before assuming your trace calls aren’t working.

Debugging locally with the Plugin Profiler

The Plugin Registration Tool includes a Profiler that can capture a real execution’s full context (target, images, configuration) and replay it against your plugin running locally in Visual Studio with a debugger attached — far more practical than guessing from trace logs alone once a bug is hard to reproduce.