๐ŸŽ“ A1Learner
๐Ÿ”
0 of 38 lessons completed

C# Inner Exceptions

An inner exception is the original error stored inside a new exception. The outer exception adds context while the first error stays available.

Print the outer message

Register catches the age error and throws a wider one. The outer catch prints the new message.

using System;

class TooYoungException : Exception
{
    public TooYoungException(string message) : base(message)
    {
    }
}

class Program
{
    static void CheckAge(int age)
    {
        if (age < 18)
        {
            throw new TooYoungException("Age " + age + " is too young");
        }
        Console.WriteLine("Age accepted");
    }

    static void Register(int age)
    {
        try
        {
            CheckAge(age);
        }
        catch (TooYoungException ex)
        {
            throw new InvalidOperationException("Registration failed", ex);
        }
        Console.WriteLine("Registered");
    }

    static void Main(string[] args)
    {
        try
        {
            Register(15);
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine("Outer: " + ex.Message);
        }
    }
}

Read the inner message

The inner exception still holds the age message. The catch prints the outer line and the inner line.

using System;

class TooYoungException : Exception
{
    public TooYoungException(string message) : base(message)
    {
    }
}

class Program
{
    static void CheckAge(int age)
    {
        if (age < 18)
        {
            throw new TooYoungException("Age " + age + " is too young");
        }
        Console.WriteLine("Age accepted");
    }

    static void Register(int age)
    {
        try
        {
            CheckAge(age);
        }
        catch (TooYoungException ex)
        {
            throw new InvalidOperationException("Registration failed", ex);
        }
        Console.WriteLine("Registered");
    }

    static void Main(string[] args)
    {
        try
        {
            Register(15);
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine("Outer: " + ex.Message);
            Console.WriteLine("Inner: " + ex.InnerException.Message);
        }
    }
}

A registration that passes

An age of 20 passes the check inside Register. Nothing is wrapped, and both success lines print.

using System;

class TooYoungException : Exception
{
    public TooYoungException(string message) : base(message)
    {
    }
}

class Program
{
    static void CheckAge(int age)
    {
        if (age < 18)
        {
            throw new TooYoungException("Age " + age + " is too young");
        }
        Console.WriteLine("Age accepted");
    }

    static void Register(int age)
    {
        try
        {
            CheckAge(age);
        }
        catch (TooYoungException ex)
        {
            throw new InvalidOperationException("Registration failed", ex);
        }
        Console.WriteLine("Registered");
    }

    static void Main(string[] args)
    {
        try
        {
            Register(20);
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine("Outer: " + ex.Message);
        }
    }
}

Example

using System;

class TooYoungException : Exception
{
    public TooYoungException(string message) : base(message)
    {
    }
}

class Program
{
    static void CheckAge(int age)
    {
        if (age < 18)
        {
            throw new TooYoungException("Age " + age + " is too young");
        }
        Console.WriteLine("Age accepted");
    }

    static void Register(int age)
    {
        try
        {
            CheckAge(age);
        }
        catch (TooYoungException ex)
        {
            throw new InvalidOperationException("Registration failed", ex);
        }
        Console.WriteLine("Registered");
    }

    static void Main(string[] args)
    {
        try
        {
            Register(14);
        }
        catch (InvalidOperationException ex) when (ex.InnerException != null)
        {
            Console.WriteLine("Outer: " + ex.Message);
            Console.WriteLine("Inner: " + ex.InnerException.Message);
        }
    }
}

Key points

  • A two-argument constructor takes a message and an exception.
  • The passed exception becomes the inner exception.
  • The InnerException property reads the stored exception.
  • A catch block inside a method can wrap the error it caught.
  • The new throw adds context for the wider step that failed.
  • The original message stays readable through the inner exception.
  • A filter can test whether an inner exception is present.

In this example, CheckAge throws a TooYoungException for an age of 14. Register catches it and throws an InvalidOperationException with the first error stored inside. The filtered outer catch prints the outer message and the inner message.

Note

The outer message names the failed step, and the inner message names the first cause.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does the InnerException property hold?