C# NullReferenceException — Enable Nullable and Find the Null
Diagnose C# NullReferenceException by reading the stack trace, enabling nullable reference types, and fixing uninitialized dependencies, LINQ defaults, and async results.
Symptoms
- System.NullReferenceException: Object reference not set to an instance of an object.
- The stack trace points at a line that dereferences a property or calls a method
- The error appears only in production or after a configuration change
- The project builds with nullable warnings disabled
Possible causes
- A dependency or service was not registered or injected, leaving a field null
- FirstOrDefault or Find returned null and the code used the result directly
- An async call returned null and the caller assumed a value
- Configuration binding produced null for a missing key, or a collection was never initialized
Diagnosis steps
Build with nullable analysis enabled
dotnet build -p:Nullable=enable -p:WarningsAsErrors=nullableInterpretation
Nullable warnings identify the exact flow where a maybe-null value is dereferenced. Treating them as errors prevents new occurrences from shipping.
Get a full stack trace and inner exception
dotnet test --logger "console;verbosity=detailed" 2>&1 | grep -A20 'NullReferenceException'Interpretation
The first application frame is the dereference site. If it is in a framework method, inspect the arguments passed into it, since the null may originate in your code.
List nullable warnings for the project
dotnet format analyzers --verify-no-changes --severity warn 2>&1 | head -40Interpretation
The analyzer output groups warnings by file and line. Recurring CS8602 warnings in one class point to an uninitialized member or constructor path.
Check dependency registration at startup
dotnet run --environment Development 2>&1 | grep -iE 'null|not registered|Unable to resolve' | head -20Interpretation
A missing DI registration often surfaces as a null field rather than a startup error when the property is optional. Validate the container at startup with ValidateOnBuild.
Root cause
A reference type variable can hold null, and C# only prevents dereferencing it at compile time when nullable reference types are enabled and warnings are respected. A field left unassigned, a lookup that returns null, or a missing dependency produces a runtime null that is dereferenced later, often far from the origin, so the stack trace shows the symptom rather than the cause.
Fix
Enable nullable reference types, annotate APIs honestly, and initialize fields or mark them required. Use ArgumentNullException.ThrowIfNull at public boundaries, and handle lookup misses explicitly instead of assuming a value. For DI, register every dependency and enable ValidateOnBuild and ValidateScopes in development. Replace null checks scattered at use sites with validation at the boundary that produced the value.
Risk note
The null-forgiving operator (!) suppresses the warning without proving anything and should be rare. Making every property nullable hides the contract and spreads checks. Adding null checks around a root cause can mask a configuration or lifecycle bug instead of fixing it.
Verify the fix
Build with nullable warnings as errors and confirm no CS8602 or related warnings remain. Run the tests that cover the failing path, then reproduce the production scenario and verify the exception no longer occurs and the code returns the expected value or a clear domain error.
The trace shows where it broke, not where it became null
System.NullReferenceException: Object reference not set to an instance of an object.
at Orders.Api.OrderService.Total(Order order) in OrderService.cs:line 42
Line 42 dereferences something null. The value may have been null since startup, or a lookup returned no match. The stack trace rarely identifies the origin.
Turn on the compiler’s null tracking
dotnet build -p:Nullable=enable -p:WarningsAsErrors=nullable
With nullable reference types enabled, the compiler warns at the assignment or flow where a maybe-null value reaches a dereference. Existing code will produce many warnings; fix them by contract, not by adding !.
<PropertyGroup>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
Fix the source of the null
public sealed class OrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository
?? throw new ArgumentNullException(nameof(repository));
}
public decimal Total(Order order)
{
ArgumentNullException.ThrowIfNull(order);
return order.Lines.Sum(line => line.Price * line.Quantity);
}
}
Handle FirstOrDefault explicitly with is null checks, validate configuration with IOptions validation, and initialize collections. If DI is the source, enable ValidateOnBuild so missing registrations fail at startup rather than at first request.
Verify
Build with nullable warnings as errors and confirm the previous warning at the dereference site is gone because the flow is now provably non-null. Run tests for the failing endpoint, then reproduce the original scenario. A clear ArgumentNullException naming the parameter is a better failure than a null dereference, and it proves the boundary is now guarded.