August 23, 2026

Corrupted State Exceptions

Critical exceptions are exceptions that should not be caught. In C#, exceptions that indicate a catastrophic failure of the runtime or the process are generally known as Corrupted State Exceptions (CSEs). You should avoid catching these because the state of the application is no longer predictable, and attempting to execute code—especially code that allocates memory for logging—can lead to secondary crashes or hang the process.

public static class ExceptionExtender
{
    /*
    The primary exceptions you should avoid catching in a general exception handler are:
    Exception	Reason
    OutOfMemoryException	Logging/documenting the exception may itself fail due to lack of available memory.
    StackOverflowException	The stack is already exhausted; attempting to execute logging code will fail.
    ExecutionEngineException	Represents a critical CLR failure; recovery is not guaranteed. OBSOLETE now.
    AccessViolationException	Indicates unsafe memory access; logging may be unsafe or unreliable.
    ThreadAbortException	Thrown by the runtime to abort a thread; catching and rethrowing can interfere with CLR cleanup.
    AppDomainUnloadedException	The application domain is being unloaded; recovery is not possible.
    BadImageFormatException	Indicates a corrupted or invalid assembly; recovery is not feasible.
    */


    public static bool IsCriticalException(this Exception ex)
    {
        return ex is OutOfMemoryException or
               StackOverflowException or
               // ExecutionEngineException or // OBSOLETE: ExecutionEngineException is obsolete and should not be used in new code. It was used to indicate a severe error in the execution engine of the .NET runtime, but it has been deprecated and is no longer relevant in modern .NET applications.
               AccessViolationException or
               ThreadAbortException or
               AppDomainUnloadedException or
               BadImageFormatException;
    }
}

Here is an example for using it:


public T? LoadFromJsonFile(string jsonFilePath) where T : class, new()
{
    Debug.Assert(jsonFilePath != null, $"Parameter {nameof(jsonFilePath)} is null");
    T? reconstituted = null;
    try
    {
        using (var stream = _fileSystem.File.OpenRead(jsonFilePath))
        {
            reconstituted = JsonSerializer.Deserialize(stream, _serializerOptions);
        }
    }
    catch (Exception ex) when (!ex.IsCriticalException())
    {
        _logger.Log(LogLevel.Error, CallerContext.Create(), "Exception deserializing JSON from file '{JsonFilePath}' of: {Exception}",
            jsonFilePath, ex);
    }
    return reconstituted;
}

No comments: