Introduction
A production application can fail in a way that leaves very little useful information behind.
An exception may escape the application's normal error-handling pipeline, a native failure may terminate the process, or the operating system may stop the process before your normal logging code can finish writing a useful record.
For these cases, ordinary application logging is not always enough.
.NET provides several layers for diagnosing process failures, including unhandled-exception events, crash dumps, EventPipe diagnostics, and, in .NET 11, runtime-generated JSON crash reports. The crash-report capability is configured through runtime environment variables and provides thread and stack-frame information when the process crashes.
The important production principle is:
Do not depend on one error-handling mechanism to diagnose every type of application failure.
Instead, combine application-level logging with runtime crash diagnostics.
Why Normal Exception Handling Is Not Enough
Most application errors can be handled normally:
try
{
await ProcessOrderAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Order processing failed");
}
This works when the exception reaches the catch block.
But some failures happen outside that path.
For example:
Application Code
|
v
Exception
|
+---- try/catch
| |
| v
| Logged
|
+---- No handler
|
v
Process termination
A process-level failure needs a different diagnostic strategy.
Microsoft's exception guidance also distinguishes handling expected application exceptions from failures that leave the process in an undefined state.
What Is a Crash Report?
A crash report is diagnostic information generated when the runtime encounters a process crash.
In .NET 11, the runtime can generate a JSON-formatted crash report when DOTNET_EnableCrashReport is enabled. The report contains information about threads and stack frames associated with the crashing application.
Conceptually:
Application
|
X
Process Crash
|
v
.NET Runtime
|
+---- Crash Report
|
+---- Crash Dump
These outputs serve different purposes.
A crash report is relatively focused diagnostic information.
A dump is a snapshot of the process state and can contain much more information, including application memory.
Enable Crash Reports in .NET 11
The runtime setting is:
DOTNET_EnableCrashReport=1
When enabled, .NET generates a JSON crash report when the runtime supports the crash-report path.
The generated filename is based on the dump path or dump name with:
.crashreport.json
appended.
For example:
export DOTNET_EnableCrashReport=1
Then start the application normally.
The setting can be configured through the deployment environment rather than hard-coded into application source code.
Combine Crash Reports With Crash Dumps
A crash report is useful, but a dump provides a much deeper diagnostic snapshot.
.NET supports automatic crash dump collection through environment variables such as:
DOTNET_DbgEnableMiniDump=1
You can also specify the dump type:
DOTNET_DbgMiniDumpType=2
The documented dump types include:
Value | Type | Purpose |
|---|---|---|
1 | Mini | Smaller diagnostic dump |
2 | Heap | More comprehensive process information |
3 | Triage | Smaller dump with reduced personal information |
4 | Full | Full process memory |
The default dump type is Heap.
For many production incidents, a heap dump provides substantially more information than a small crash report.
However, larger dumps also have larger storage and security implications.
Configure the Dump Location
The dump path can be controlled with:
DOTNET_DbgMiniDumpName=/var/log/myapp/crash-%p-%t.dmp
.NET supports template values in dump filenames.
For example:
%p = Process ID
%e = Executable name
%h = Host name
%t = Timestamp
This is useful when several instances write diagnostic files to the same directory.
A containerized deployment might use a writable diagnostic directory:
/diagnostics/
crash-1234-1720000000.dmp
crash-1234-1720000000.dmp.crashreport.json
The directory must be writable by the process account.
A Practical Production Configuration
A Linux deployment could use:
DOTNET_EnableCrashReport=1
DOTNET_DbgEnableMiniDump=1
DOTNET_DbgMiniDumpType=2
DOTNET_DbgMiniDumpName=/diagnostics/crash-%p-%t.dmp
This provides two diagnostic artifacts:
Crash
|
+---- .crashreport.json
|
+---- .dmp
The JSON report gives quick information.
The dump can be used for deeper investigation.
Why You Should Not Rely Only on AppDomain.UnhandledException
.NET provides:
AppDomain.CurrentDomain.UnhandledException
It can be useful for logging an exception that has escaped normal exception handling.
For example:
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
{
if (args.ExceptionObject is Exception ex)
{
Console.Error.WriteLine(ex);
}
};
However, this should not be treated as a universal crash-capture mechanism.
The event is a notification for uncaught exceptions. Microsoft also cautions that the handler runs during application termination and should avoid operations that can block or introduce additional failures.
A logging call that depends on a failing database, network connection, or another unhealthy subsystem may therefore be unreliable during a crash.
Keep Crash Handlers Simple
A crash-path handler should do as little work as possible.
Avoid:
AppDomain.CurrentDomain.UnhandledException += async (_, args) =>
{
await SendToDatabaseAsync(args);
await NotifyExternalServiceAsync(args);
};
This introduces additional dependencies into the failure path.
Prefer a simple local diagnostic operation:
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
{
Console.Error.WriteLine(
$"Unhandled exception: {args.ExceptionObject}");
};
The runtime-level crash report or dump should provide the deeper diagnostic information.
ASP.NET Core Errors Are a Different Layer
ASP.NET Core applications have their own request-level error-handling pipeline.
For example:
HTTP Request
|
v
Middleware
|
+---- Exception
|
v
Error Handler
|
v
HTTP 500
A request exception does not necessarily mean that the entire process has crashed.
The ASP.NET Core developer exception page and exception-handling middleware operate at the HTTP pipeline level.
Therefore, an ASP.NET Core production application should typically have:
Request Errors
|
v
Application Logging
Process Crashes
|
+---- Crash Report
|
+---- Crash Dump
These mechanisms solve different problems.
What About Background Services?
Background workers are another important source of failures.
Consider:
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await ProcessAsync(stoppingToken);
}
}
}
A failure inside the worker needs to be handled according to the intended lifecycle of that service.
For recoverable operation failures:
try
{
await ProcessAsync(stoppingToken);
}
catch (Exception ex)
{
logger.LogError(ex, "Worker operation failed");
}
For a failure that makes the process unsafe to continue, allowing the process to terminate may be preferable to leaving the application in an unknown state.
The crash diagnostics then provide evidence for investigating the failure.
Native Crashes Need Different Thinking
Not every process termination is a managed exception.
A native dependency can cause a process-level failure without passing through a normal C# catch block.
For example:
C# Application
|
v
Native Library
|
X
Native Crash
|
v
Process Terminates
This is where crash dumps become especially useful.
A dump contains a snapshot of process state that can be analyzed after the process has terminated.
Use dotnet-dump for Manual Investigation
Automatic crash dumps are useful for unexpected failures, but sometimes the application is still alive and unhealthy.
For example:
High CPU
Deadlock
Memory pressure
Thread starvation
Unresponsive application
In these situations, use dotnet-dump to collect a dump from the running process.
A basic command is:
dotnet-dump collect -p <process-id>
Microsoft documents dotnet-dump as a cross-platform diagnostic tool for collecting and analyzing .NET process dumps.
This is different from automatic crash collection.
Automatic crash
|
v
Runtime creates dump
Suspected unhealthy process
|
v
Engineer collects dump
Both approaches are useful.
Be Careful With Containers
Crash dumps can be large.
Microsoft notes that collecting full or heap dumps can cause substantial virtual memory to be paged into the process and, in a memory-constrained container, this additional memory pressure can cause the container to be terminated.
Therefore, test dump collection under realistic resource limits.
For example:
Container
|
+---- Application Memory
|
+---- Runtime
|
+---- Dump Collection
|
v
Extra Memory
Do not discover this behavior for the first time during a production incident.
Protect Crash Dumps
A dump can contain sensitive information because it may include process memory.
That can include:
User data
Access tokens
Connection information
Request data
Configuration values
Secrets held in memory
Microsoft explicitly warns that dumps may contain sensitive information and should be handled according to the application's security requirements.
Treat crash dumps as sensitive production artifacts.
Use:
Restricted filesystem permissions
Encrypted storage
Controlled retention
Limited access
Secure transfer
Automatic cleanup
Do not upload production dumps to an unrestricted shared location.
Add Crash Correlation
A useful production design combines normal logs with crash artifacts.
For example:
Request
|
v
Trace ID
|
+---- Application Logs
|
+---- Metrics
|
+---- Crash Report
|
+---- Crash Dump
If the process crashes, the surrounding logs can provide the operational context while the crash artifacts provide process-level information.
Where possible, include application version and deployment information in your logs:
Version: 2026.09.28.4
Environment: Production
Instance: app-07
This helps determine whether the crash corresponds to a particular deployment.
Test the Crash Pipeline
Do not wait for a real crash to discover that the dump directory is not writable.
Create a controlled test environment and deliberately trigger a process failure.
For example:
Environment.FailFast(
"Crash diagnostics test");
This should be done only in a controlled test environment.
Then verify:
[ ] Process terminates
[ ] Crash report appears
[ ] Dump appears
[ ] Filename is correct
[ ] File permissions are correct
[ ] Diagnostic artifact is readable
[ ] Logs identify the deployment
[ ] Artifact retention works
This validates the entire pipeline.
Common Mistakes
Depending Only on try/catch
A process-level failure may never reach the application's exception handler.
Depending Only on UnhandledException
The event provides notification, but it is not equivalent to a complete process dump.
Performing Network Operations During a Crash
The failing component may be the network, database, or another dependency.
Creating Full Dumps Automatically Without Capacity Planning
Large dumps can consume significant storage and resources.
Storing Dumps Indefinitely
Dumps can contain sensitive memory contents.
Exposing Diagnostic Endpoints Publicly
Diagnostic interfaces should be protected.
Never Testing Crash Collection
A configuration that has never been tested is not a reliable incident-response mechanism.
Best Practices
Keep normal exception handling for recoverable application errors.
Use runtime crash reports for process-level diagnostic information.
Enable automatic crash dumps for services where post-crash investigation is important.
Select the smallest dump type that provides the required diagnostic information.
Protect crash artifacts as sensitive data.
Use
dotnet-dumpfor manual investigation of unhealthy but still-running processes.Test crash collection in the same deployment environment used in production.
Verify container memory and storage limits before enabling automatic dumps.
Keep crash-path logging simple and local.
Correlate crash artifacts with application version and deployment information.
Establish retention and deletion policies for dumps.
Document who can access production diagnostic artifacts.
Crash Report vs Crash Dump
Capability | Crash Report | Crash Dump |
|---|---|---|
JSON format | Yes | No |
Thread information | Yes | Yes |
Stack information | Yes | Yes |
Process memory | No | Depending on dump type |
Easy to inspect | Yes | Requires diagnostic tools |
Storage size | Relatively small | Can be large |
Deep investigation | Limited | Stronger |
Sensitive data exposure | Lower | Potentially high |
The appropriate choice depends on the incident.
A crash report may be enough to identify the failing thread or stack.
A dump is more useful when you need to inspect application state in depth.
Production Configuration Checklist
Crash Reporting
[ ] DOTNET_EnableCrashReport configured where supported
[ ] Crash report location is writable
[ ] Crash report retention is defined
Crash Dumps
[ ] DOTNET_DbgEnableMiniDump configured where required
[ ] Dump type selected intentionally
[ ] Dump location has sufficient storage
[ ] Container memory limits tested
[ ] Dump permissions are restricted
Application Logging
[ ] Unhandled exceptions are logged
[ ] Background worker failures are logged
[ ] Deployment/version information is available
[ ] Logs are correlated with instance information
Security
[ ] Crash artifacts are treated as sensitive
[ ] Access is restricted
[ ] Storage is encrypted
[ ] Retention policy exists
[ ] Automatic cleanup is configured
Testing
[ ] Controlled crash has been tested
[ ] Crash report was generated
[ ] Dump was generated
[ ] Dump can be analyzed
[ ] Recovery behavior was verified
Summary
.NET 11 provides another useful layer for diagnosing process failures through runtime-generated crash reports. Setting DOTNET_EnableCrashReport=1 enables a JSON-formatted crash report containing thread and stack-frame information for supported crash scenarios.
For deeper investigation, automatic crash dumps can capture substantially more process state. .NET supports configuring these through environment variables such as DOTNET_DbgEnableMiniDump, DOTNET_DbgMiniDumpType, and DOTNET_DbgMiniDumpName.
The practical architecture is:
Expected Application Error
|
v
try/catch + Application Logging
Unexpected Process Failure
|
+---- Crash Report
|
+---- Crash Dump
|
v
Post-Crash Investigation
The important part is not simply enabling a runtime variable. A production-ready crash-diagnostic system also needs secure storage, controlled retention, sufficient disk and memory capacity, and a tested investigation workflow.
When the application exits unexpectedly, the goal is to preserve enough evidence to answer three questions:
What failed?
Where did it fail?
What was the process doing when it failed?
With application logging, crash reports, and appropriately configured dumps working together, those questions become much easier to answer.
Meta Description
SEO/GEO Keywords
Category
.NET

Join the conversation! Your thoughts help the community grow.