C#
NullReferenceException
type initializer
error handling
software development

Why would finding a type's initializer throw a NullReferenceException?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A TypeInitializationException wrapping a NullReferenceException occurs when a static constructor (type initializer) dereferences a null reference during class initialization. This happens before any instance of the class is created — when the runtime first accesses the type. The confusing part is that the error appears to come from the line that uses the class, not from the static constructor itself. You must inspect the InnerException of the TypeInitializationException to find the actual NullReferenceException and its stack trace.

The Error

 
System.TypeInitializationException: The type initializer for 'MyClass' threw an exception.
 ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at MyClass..cctor() in MyClass.cs:line 8

The .cctor() in the stack trace refers to the static constructor (class constructor / type initializer).

How Type Initializers Work

csharp
1public class Config
2{
3    // Static field initializers run as part of the type initializer
4    private static readonly string connectionString = LoadConnectionString();
5    private static readonly int timeout = int.Parse(GetSetting("Timeout"));
6
7    // Static constructor — also part of the type initializer
8    static Config()
9    {
10        Console.WriteLine("Config type initializer running");
11    }
12
13    private static string LoadConnectionString() { /* ... */ }
14    private static string GetSetting(string key) { /* ... */ }
15}

The type initializer runs once, the first time the type is accessed (creating an instance, calling a static method, or reading a static field). If it throws, the type is permanently broken for the lifetime of the application domain.

Common Cause 1: Null Static Field Used in Initializer

csharp
1public class AppConfig
2{
3    // Order matters! These run top-to-bottom
4    private static readonly string basePath = GetBasePath();  // Returns null
5    private static readonly string configFile = basePath.TrimEnd('/') + "/config.json";
6    // NullReferenceException: basePath is null, calling TrimEnd on null
7
8    private static string GetBasePath()
9    {
10        return Environment.GetEnvironmentVariable("APP_BASE_PATH");
11        // Returns null if env var is not set
12    }
13}
14
15// Any access to AppConfig triggers the error:
16var x = new AppConfig();  // TypeInitializationException

Fix: Null Check in Initializer

csharp
1public class AppConfig
2{
3    private static readonly string basePath = GetBasePath() ?? "/default/path";
4    private static readonly string configFile = basePath.TrimEnd('/') + "/config.json";
5
6    private static string GetBasePath()
7    {
8        return Environment.GetEnvironmentVariable("APP_BASE_PATH");
9    }
10}

Common Cause 2: Static Field Initialization Order

csharp
1public class Database
2{
3    // WRONG: connectionString uses settings, but settings hasn't been initialized yet
4    private static readonly Dictionary<string, string> settings = LoadSettings();
5    private static readonly string connectionString = settings["ConnectionString"];
6    // NullReferenceException if LoadSettings() returns null
7
8    // Even if LoadSettings works, the key might not exist:
9    // KeyNotFoundException, wrapped in TypeInitializationException
10
11    private static Dictionary<string, string> LoadSettings()
12    {
13        // Returns null on failure
14        try { return JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText("settings.json")); }
15        catch { return null; }
16    }
17}

Fix: Validate in Static Constructor

csharp
1public class Database
2{
3    private static readonly Dictionary<string, string> settings;
4    private static readonly string connectionString;
5
6    static Database()
7    {
8        settings = LoadSettings();
9        if (settings == null)
10            throw new InvalidOperationException("Failed to load settings.json");
11
12        if (!settings.TryGetValue("ConnectionString", out connectionString))
13            throw new InvalidOperationException("ConnectionString not found in settings");
14    }
15}

Common Cause 3: Dependency Between Static Fields Across Classes

csharp
1public class ClassA
2{
3    public static readonly string Value = ClassB.Helper;  // Triggers ClassB initializer
4}
5
6public class ClassB
7{
8    public static readonly string Helper = ClassA.Value;  // Triggers ClassA initializer (circular!)
9}
10
11// Accessing either class causes unpredictable behavior:
12// One of them will see null because the other hasn't finished initializing
13Console.WriteLine(ClassA.Value);  // May be null → NullReferenceException in dependent code

Fix: Break the Circular Dependency

csharp
1public static class SharedConfig
2{
3    public static readonly string Value = "shared";
4}
5
6public class ClassA
7{
8    public static readonly string Value = SharedConfig.Value;
9}
10
11public class ClassB
12{
13    public static readonly string Helper = SharedConfig.Value;
14}

Debugging Type Initializer Failures

csharp
1try
2{
3    var instance = new MyClass();
4}
5catch (TypeInitializationException ex)
6{
7    Console.WriteLine($"Type: {ex.TypeName}");
8    Console.WriteLine($"Inner: {ex.InnerException}");
9    Console.WriteLine($"Stack: {ex.InnerException?.StackTrace}");
10    // The InnerException contains the actual NullReferenceException
11    // with the correct line number
12}
csharp
1// After a TypeInitializationException, the type is PERMANENTLY broken
2try { var x = new MyClass(); }
3catch (TypeInitializationException) { }
4
5// Second attempt ALSO throws TypeInitializationException
6// The static constructor does NOT retry
7try { var y = new MyClass(); }
8catch (TypeInitializationException ex)
9{
10    // Same error — the runtime remembers the failure
11    Console.WriteLine("Type is permanently broken");
12}

Using Lazy Initialization to Avoid Type Initializer Issues

csharp
1public class SafeConfig
2{
3    // Lazy<T> defers initialization until first access
4    // and provides clear exception handling
5    private static readonly Lazy<string> _connectionString = new Lazy<string>(() =>
6    {
7        var value = Environment.GetEnvironmentVariable("DB_CONNECTION");
8        return value ?? throw new InvalidOperationException("DB_CONNECTION not set");
9    });
10
11    public static string ConnectionString => _connectionString.Value;
12}

Lazy<T> throws the inner exception directly (not wrapped in TypeInitializationException), making debugging easier.

Common Pitfalls

  • Not checking InnerException: The TypeInitializationException itself is not informative. The actual error (NullReferenceException, FileNotFoundException, etc.) is in InnerException. Always log or inspect ex.InnerException when catching TypeInitializationException.
  • Type is permanently broken after failure: Once a type initializer throws, every subsequent access to that type throws TypeInitializationException without retrying the initializer. The only recovery is restarting the application (or AppDomain). Design initializers to be robust.
  • Relying on environment variables in static initializers: Environment variables may not be set in all environments (local dev, CI, production). A null return from Environment.GetEnvironmentVariable() causes NullReferenceException in static initializers. Always provide defaults or throw descriptive exceptions.
  • Circular dependencies between static types: If ClassA's initializer references ClassB and ClassB's initializer references ClassA, one of them sees uninitialized (default/null) values. The CLR does not detect or prevent circular static initialization — it just runs them in an undefined order.
  • Swallowing exceptions in static methods called from initializers: If LoadSettings() catches exceptions and returns null, the static initializer proceeds with null, causing a NullReferenceException later. Either let exceptions propagate or return a valid default value.

Summary

  • TypeInitializationException wrapping NullReferenceException means a static constructor dereferenced null
  • Always inspect InnerException for the real error and stack trace
  • Static field initializers run top-to-bottom — order matters for dependencies
  • The type is permanently broken after a failed initializer — no retry within the same AppDomain
  • Avoid circular dependencies between static fields of different classes
  • Use Lazy<T> for deferred initialization with clearer exception handling
  • Validate all external inputs (environment variables, config files) in static constructors with null checks

Course illustration
Course illustration

All Rights Reserved.