Free Handbook · Every example compiled & verified

Errors & Debugging

Read C# compiler errors and .NET stack traces, fix the 15 errors every beginner hits, and debug with breakpoints, nullable warnings and logging.

0 / 142 lessons🔥 0 day streak
ShareXLinkedIn

Module 11 · what you'll be able to do

  • Tell a compile-time error (CSxxxx, nothing runs) from a runtime exception (the program ran and crashed)
  • Read a compiler diagnostic by file, line, column and code, and a .NET stack trace from the throw site down to Main
  • Recognise and fix the 15 most common C# errors on sight, from CS0103 to KeyNotFoundException
  • Use nullable reference type warnings to catch NullReferenceException before the code runs
  • Debug with breakpoints, watches and conditional breakpoints, and add logging that survives into production
01

Compile errors versus runtime exceptions

C# problems arrive at two very different moments. A compile error is the compiler (Roslyn) refusing to build your program: the syntax is wrong, a name does not exist, or the types do not fit. Nothing runs at all. A runtime exception happens while the program is running: the code was valid, but the data was not what you expected — a null, a missing key, text that is not a number. Knowing which one you are looking at tells you where to look.

Compile error

  • Reported by dotnet build / dotnet run before anything executes
  • Format: File.cs(line,col): error CS0103: …
  • Always happens, every time, on every machine
  • The editor underlines it in red as you type
  • Fix the code the message points at

Runtime exception

  • Thrown while the program runs; everything before it already happened
  • Format: Unhandled exception. System.XxxException: message + stack trace
  • May depend on input, data, timing or environment
  • Invisible until that line runs with that data
  • Fix the data flow that led there, or handle the case

Anatomy of a compiler error

text
Program.cs(6,19): error CS0103: The name 'grade' does not exist in the current context
Program.cs(4,12): warning CS0219: The variable 'grade' is assigned but its value is never used

The build failed. Fix the build errors and run again.

File, (line,column), severity, the CS code, the message. Warnings do not stop the build; errors do.

  • Fix the first error first. One missing brace can produce twenty follow-on errors that vanish once it is fixed.
  • Search the code. Every CS number has a page on Microsoft Learn with examples — searching "CS0266" is faster than searching the message.
  • Read warnings too. The warning above is a clue to the error: the variable exists, just not where it is used.

Reading a .NET stack trace

This program calls PlaceOrder, which calls UnitPrice, which calls Divide. The second quantity is 0, and here is what .NET printed:

C#Program.cs
PlaceOrder(new[] { 2, 0, 1 });

static void PlaceOrder(int[] quantities)
{
    foreach (int q in quantities)
        Console.WriteLine(UnitPrice(100, q));
}

static int UnitPrice(int total, int qty)
{
    return Divide(total, qty);
}

static int Divide(int a, int b) => a / b;
text
50
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
   at Program.<<Main>$>g__Divide|0_2(Int32 a, Int32 b) in Program.cs:line 14
   at Program.<<Main>$>g__UnitPrice|0_1(Int32 total, Int32 qty) in Program.cs:line 11
   at Program.<<Main>$>g__PlaceOrder|0_0(Int32[] quantities) in Program.cs:line 6
   at Program.<Main>$(String[] args) in Program.cs:line 1

Real output. The first line printed (50) shows the first order worked; the crash came on the second.

  1. 1
    1. First line: what went wrong

    The exception type and message: DivideByZeroException: Attempted to divide by zero.

  2. 2
    2. Bottom line: where the story starts

    <Main>$ is your top-level statements, line 1. Reading upwards gives the chain of calls that led to the crash: Main → PlaceOrder → UnitPrice → Divide.

  3. 3
    3. Top "at" line: where it was thrown

    Line 14, inside Divide. Names like <<Main>$>g__Divide|0_2 are how the compiler names local functions; read them as "Divide".

  4. 4
    4. Find the first frame that is YOUR mistake

    Divide did what it was told. The real question is why qty was 0 — so the fix probably belongs in UnitPrice or PlaceOrder, not in Divide.

  5. 5
    5. Inner exceptions

    A line starting ---> introduces an inner exception: the original cause that was wrapped by another. The innermost one is usually the one to fix.

You can also catch an exception and inspect it in code: its type, Message, and InnerException. This is what logging frameworks do for you.

C#Program.cs
try
{
    LoadConfig("port=abc");
}
catch (Exception ex)
{
    Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
    Console.WriteLine($"caused by {ex.InnerException?.GetType().Name}: {ex.InnerException?.Message}");
}

static int LoadConfig(string line)
{
    string value = line.Split('=')[1];
    try
    {
        return int.Parse(value);
    }
    catch (FormatException fe)
    {
        throw new InvalidOperationException($"bad config line '{line}'", fe);   // keep the cause
    }
}
Outputcompiled & run with real C#
InvalidOperationException: bad config line 'port=abc'
caused by FormatException: The input string 'abc' was not in a correct format.
Your turn

Remove the fe argument from the throw and run it again. What does the second line print now, and why is that worse when you are debugging production logs?

02

Compile errors 1–5: names, members and syntax

These five come from the compiler not being able to find something — a variable, a type, a member, or the end of a statement.

Error you will hit

1. CS0103: the name does not exist in the current context

C#
int score = 72;
if (score >= 50)
{
    string grade = "pass";
}
Console.WriteLine(grade);
Program.cs(6,19): error CS0103: The name 'grade' does not exist in the current context
Program.cs(4,12): warning CS0219: The variable 'grade' is assigned but its value is never used
Why the compiler said that

A variable lives only inside the { } block where it is declared — its scope. grade is gone once the if block ends. The same error appears for a simple typo or wrong capitalisation (Grade is not grade).

The fix

Declare the variable in the outer scope, before the block, and assign it inside.

C#
int score = 72;
string grade = "fail";
if (score >= 50)
{
    grade = "pass";
}
Console.WriteLine(grade);
Error you will hit

2. CS0246: type or namespace not found

C#
var sb = new StringBuilder();
sb.Append("hello");
Console.WriteLine(sb);
Program.cs(1,14): error CS0246: The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)
Why the compiler said that

StringBuilder lives in the System.Text namespace, which is not one of the implicit usings (those are System, System.IO, System.Collections.Generic, System.Linq, System.Net.Http, System.Threading and System.Threading.Tasks). The other common cause is a NuGet package that was never added.

The fix

Add the using directive (your editor's quick fix, Ctrl+. in Visual Studio and VS Code, does it for you) or install the package with dotnet add package.

C#
using System.Text;

var sb = new StringBuilder();
sb.Append("hello");
Console.WriteLine(sb);
Error you will hit

3. CS1002: ; expected

C#
Console.WriteLine("Hello")
Console.WriteLine("World");
Program.cs(1,27): error CS1002: ; expected
Why the compiler said that

Every C# statement ends with a semicolon. The compiler reports the column just after the last thing it understood — line 1, column 27 — which is exactly where the ; should be.

The fix

Add the semicolon at the reported position. If the reported line looks fine, check the end of the line above it.

C#
Console.WriteLine("Hello");
Console.WriteLine("World");
Error you will hit

4. CS1061: type does not contain a definition for a member

C#
string word = "handbook";
Console.WriteLine(word.Lenght);
Program.cs(2,24): error CS1061: 'string' does not contain a definition for 'Lenght' and no accessible extension method 'Lenght' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
Why the compiler said that

The compiler knows word is a string and checked every member a string has: there is no Lenght. Typos, wrong casing (length) and calling a List method on an array (arr.Count instead of arr.Length) all produce CS1061. The "extension method" part means a missing using can cause it too — LINQ methods disappear without using System.Linq.

The fix

Correct the member name. Type the dot and let IntelliSense list what actually exists.

C#
string word = "handbook";
Console.WriteLine(word.Length);
Error you will hit

5. CS0120: an object reference is required for a non-static member

C#
Counter.Increment();

class Counter
{
    private int count;
    public void Increment() => count++;
}
Program.cs(1,1): error CS0120: An object reference is required for the non-static field, method, or property 'Counter.Increment()'
Why the compiler said that

Increment is an instance method: it changes this counter's count. Calling it on the class name asks "which counter?" and there is no answer. Only static members can be called on the class itself. Beginners also meet this when a static method tries to use an instance field.

The fix

Create an object with new and call the method on it — or, if the member really belongs to the class rather than to each object, make it static.

C#
var counter = new Counter();
counter.Increment();
counter.Increment();
Console.WriteLine(counter.Count);

class Counter
{
    public int Count { get; private set; }
    public void Increment() => Count++;
}
03

Compile errors 6–10: types and definite assignment

C# is statically typed: every expression has a type the compiler knows, and it refuses conversions that could lose data or make no sense. It also tracks whether every variable definitely has a value and whether every path through a method returns one.

Error you will hit

6. CS0029: cannot implicitly convert string to int

C#
Console.Write("Age: ");
int age = Console.ReadLine();
Console.WriteLine(age + 1);
Program.cs(2,11): error CS0029: Cannot implicitly convert type 'string' to 'int'
Why the compiler said that

Console.ReadLine() always returns text, even if the user typed digits. C# never turns text into a number behind your back, because the text might be "twelve".

The fix

Parse it — and because input can be anything, prefer int.TryParse, which returns false instead of throwing.

C#
Console.Write("Age: ");
if (int.TryParse(Console.ReadLine(), out int age))
    Console.WriteLine(age + 1);
else
    Console.WriteLine("please type a whole number");
Error you will hit

7. CS0266: cannot implicitly convert double to int

C#
int[] marks = { 70, 85, 90 };
int average = marks.Sum() / 3.0;
Console.WriteLine(average);
Program.cs(2,15): error CS0266: Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?)
Why the compiler said that

Dividing by 3.0 makes the result a double (81.66…). Storing that in an int would silently drop the fraction, so C# demands you say so. "An explicit conversion exists" is the compiler telling you a cast would compile.

The fix

Decide what you want: keep the fraction in a double, round deliberately with Math.Round, or truncate with a cast (int).

C#
int[] marks = { 70, 85, 90 };
double average = marks.Sum() / 3.0;
int rounded = (int)Math.Round(average);
Console.WriteLine(rounded);
Error you will hit

8. CS0019: operator cannot be applied to these operand types

C#
string input = "5";
if (input == 5)
    Console.WriteLine("five");
Program.cs(2,5): error CS0019: Operator '==' cannot be applied to operands of type 'string' and 'int'
Why the compiler said that

There is no == that compares a string with a number. JavaScript would quietly convert one side; C# makes you choose. You also get CS0019 for things like bool && int or subtracting two strings.

The fix

Convert one side so both have the same type — usually parse the string, or compare against the string "5".

C#
string input = "5";
if (int.TryParse(input, out int n) && n == 5)
    Console.WriteLine("five");
Error you will hit

9. CS0165: use of unassigned local variable

C#
int discount;
bool member = true;
if (member)
    discount = 10;
Console.WriteLine(discount);
Program.cs(5,19): error CS0165: Use of unassigned local variable 'discount'
Why the compiler said that

Local variables have no default value in C#. The compiler checks every path to line 5: when member is false, discount was never assigned. It does not care that this run sets member to true — it checks all possible runs.

The fix

Give the variable a value on every path, most simply at the declaration.

C#
int discount = 0;
bool member = true;
if (member)
    discount = 10;
Console.WriteLine(discount);
Error you will hit

10. CS0161: not all code paths return a value

C#
Console.WriteLine(Grade(72));

static string Grade(int score)
{
    if (score >= 90) return "A";
    else if (score >= 50) return "Pass";
}
Program.cs(3,15): error CS0161: 'Grade(int)': not all code paths return a value
Why the compiler said that

A score of 30 reaches the closing brace without a return. A method that promises a string must return one on every path, including the ones you think never happen.

The fix

Add a final else or a return after the if chain. If a path really is impossible, throw there instead so you hear about it.

C#
Console.WriteLine(Grade(72));

static string Grade(int score)
{
    if (score >= 90) return "A";
    else if (score >= 50) return "Pass";
    else return "Fail";
}
04

Runtime exceptions 11–15

These programs all compile. They crash only when the bad line runs with the bad data. Each shows the real Unhandled exception output from .NET 10.

Error you will hit

11. NullReferenceException

C#
var order = new Order { Id = 7 };
Console.WriteLine(order.Customer.Name.ToUpper());

class Customer { public string Name { get; set; } = ""; }
class Order
{
    public int Id { get; set; }
    public Customer? Customer { get; set; }
}
Program.cs(2,19): warning CS8602: Dereference of a possibly null reference.
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at Program.<Main>$(String[] args) in Program.cs:line 2
Why the compiler said that

Customer was never set, so it is null, and .Name on null has nothing to read. Note the warning on the first line: the compiler saw the ? on Customer? and warned before the program even ran. With a chain like a.b.c.d, the line number alone does not say which part was null — check each one.

The fix

Decide what "no customer" means. Guard with ?. and ?? for a fallback, or make it impossible by requiring the value in a constructor.

C#
var order = new Order { Id = 7 };
Console.WriteLine(order.Customer?.Name.ToUpper() ?? "(guest)");

class Customer { public string Name { get; set; } = ""; }
class Order
{
    public int Id { get; set; }
    public Customer? Customer { get; set; }
}
Error you will hit

12. IndexOutOfRangeException: args[0] with no arguments

C#
string path = args[0];
Console.WriteLine($"reading {path}");
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Program.<Main>$(String[] args) in Program.cs:line 1
Why the compiler said that

args holds the command-line arguments. Run as plain dotnet run, it is an empty array, and index 0 does not exist. The same exception comes from any array index < 0 or >= Length; lists throw the similar ArgumentOutOfRangeException.

The fix

Check Length before indexing, and print a usage message when the input is missing.

C#
if (args.Length == 0)
{
    Console.WriteLine("usage: dotnet run -- <path>");
    return;
}
string path = args[0];
Console.WriteLine($"reading {path}");
Error you will hit

13. InvalidCastException: unboxing to the wrong type

C#
object boxed = "42";
int n = (int)boxed;
Console.WriteLine(n + 1);
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Int32'.
   at Program.<Main>$(String[] args) in Program.cs:line 2
Why the compiler said that

A cast does not convert; it asserts "this object already is an int". The object is a string that looks like a number, so the assertion fails at runtime. Values read from object-typed APIs (old collections, DataRow, JSON as object) are the usual source.

The fix

Use pattern matching to test the type safely, and parse when the value is text.

C#
object boxed = "42";
int n = boxed switch
{
    int i => i,
    string s when int.TryParse(s, out int parsed) => parsed,
    _ => 0,
};
Console.WriteLine(n + 1);
Error you will hit

14. KeyNotFoundException

C#
var prices = new Dictionary<string, decimal>
{
    ["apple"] = 0.5m,
    ["bread"] = 2.25m,
};
Console.WriteLine(prices["milk"]);
Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'milk' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Program.<Main>$(String[] args) in Program.cs:line 6
Why the compiler said that

Reading a dictionary with [key] means "this key must exist". The top frame is inside .NET (get_Item is the indexer), so look one frame down for your line. Keys are case-sensitive by default too: "Apple" would also fail.

The fix

Use TryGetValue (or GetValueOrDefault) when a key may be missing. See Module 08.

C#
var prices = new Dictionary<string, decimal>
{
    ["apple"] = 0.5m,
    ["bread"] = 2.25m,
};
if (prices.TryGetValue("milk", out decimal price))
    Console.WriteLine(price);
else
    Console.WriteLine("milk is not on the price list");
Error you will hit

15. FormatException: parsing a number with separators

C#
string raw = "1,500";
int amount = int.Parse(raw);
Console.WriteLine(amount * 2);
Unhandled exception. System.FormatException: The input string '1,500' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Int32.Parse(String s)
   at Program.<Main>$(String[] args) in Program.cs:line 2
Why the compiler said that

By default int.Parse accepts an optional sign and digits only — no thousands separators, no decimals, no currency symbol. Data from spreadsheets and forms often has them. And separators differ by culture: "1,500" is fifteen hundred in the US and India but one and a half in Germany.

The fix

Say what format you accept with NumberStyles and a fixed culture, and use TryParse for anything a person typed.

C#
using System.Globalization;

string raw = "1,500";
if (int.TryParse(raw, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out int amount))
    Console.WriteLine(amount * 2);
else
    Console.WriteLine($"not a number: {raw}");
Two more you will meet
InvalidOperationException: Collection was modified (changing a list inside its own foreach) is covered in Module 02, and the LINQ First() / Single() versions in Module 09. Async-specific ones — AggregateException and TaskCanceledException — are in Module 10.
05

Nullable warnings: catching nulls before they run

NullReferenceException is the most common .NET crash, so modern C# gives you a tool against it: nullable reference types, on by default in new projects (<Nullable>enable</Nullable>) and in file-based apps. With it on, string means "never null" and string? means "might be null", and the compiler tracks which is which through your code. These are warnings, so the program still builds — which is exactly why people ignore them. Do not.

C#Program.cs
string email = FindEmail(2);
Console.WriteLine(email.Length);

static string? FindEmail(int id) => id == 1 ? "[email protected]" : null;
text
Program.cs(1,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
Program.cs(2,19): warning CS8602: Dereference of a possibly null reference.

Real dotnet build output. Both warnings point at the crash that line 2 would cause at runtime.

C#Program.cs
string? email = FindEmail(2);

// 1. check, and the compiler knows it is not null inside the if
if (email is not null)
    Console.WriteLine(email.Length);

// 2. ?. returns null instead of throwing, ?? supplies a fallback
int length = email?.Length ?? 0;
Console.WriteLine(length);

// 3. fail fast with a clear message at the boundary
string required = FindEmail(1) ?? throw new InvalidOperationException("user 1 has no email");
Console.WriteLine(required);

static string? FindEmail(int id) => id == 1 ? "[email protected]" : null;
Outputcompiled & run with real C#
0
[email protected]
Your turn

Add Console.WriteLine(email!.Length); at the end. It compiles with no warning — what happens when it runs, and what did the ! actually do?

The ! operator is a promise, not a check
email! (the null-forgiving operator) only silences the warning. If you are wrong, you get the same NullReferenceException. Use it where you know something the compiler cannot, and prefer a check when you do not.
xmlMyApp.csproj
<PropertyGroup>
  <Nullable>enable</Nullable>
  <!-- make the null warnings (and all others) fail the build -->
  <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  <!-- or only some of them -->
  <WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>

Teams that turn warnings into errors stop NullReferenceException bugs at code review instead of in production.

06

Debugging tools: breakpoints, Debug.WriteLine and logging

Reading the error gets you to a line. When the line looks right and the result is still wrong, you need to watch the program run. The debugger is the fastest way; it is built into Visual Studio and Rider, and into VS Code with the C# Dev Kit extension.

  1. 1
    Set a breakpoint

    Click the margin next to a line (F9). Start with debugging (F5); execution pauses before that line runs.

  2. 2
    Look at the state

    Hover over variables, or use the Locals and Watch windows. Add an expression such as orders.Count(o => o.Total > 100) to Watch to evaluate it live.

  3. 3
    Step

    Step Over (F10) runs the line; Step Into (F11) goes into the method it calls; Step Out (Shift+F11) finishes the current method. The Call Stack window is the stack trace, live.

  4. 4
    Conditional breakpoints

    Right-click a breakpoint and add a condition (i == 997) or a hit count, so a loop stops only on the iteration that goes wrong.

  5. 5
    Break on exceptions

    In the Exception Settings, tick an exception type (e.g. NullReferenceException) to stop the moment it is thrown, with every variable still in view — even if something later catches it.

Debug.WriteLine versus Console.WriteLine

Console.WriteLine goes to the program's output, which your users see. System.Diagnostics.Debug.WriteLine goes to attached trace listeners — the debugger's Output window — and calls to it are removed entirely from Release builds. So it is safe to leave in, but it prints nothing to the console:

C#Program.cs
using System.Diagnostics;

int[] cart = { 250, 120, 80 };
int total = 0;
foreach (int price in cart)
{
    total += price;
    Debug.WriteLine($"added {price}, running total {total}");   // Output window only
}
Console.WriteLine($"Total: {total}");
Outputcompiled & run with real C#
Total: 450

Only the Console line reaches stdout. Run this under the debugger and the three Debug lines appear in the Output window.

Logging for code you cannot attach a debugger to

In production there is no debugger. Real .NET apps log through ILogger<T> from Microsoft.Extensions.Logging, which ASP.NET Core wires up for you. Use message templates with named placeholders rather than string interpolation: the values are stored as fields, so a log system can search for every line where OrderId is 42.

C#OrderService.cs
public class OrderService(ILogger<OrderService> logger, AppDb db)
{
    public async Task<Order?> GetAsync(int orderId)
    {
        logger.LogInformation("Loading order {OrderId}", orderId);
        var order = await db.Orders.FindAsync(orderId);
        if (order is null)
            logger.LogWarning("Order {OrderId} not found", orderId);
        return order;
    }

    public async Task CancelAsync(int orderId)
    {
        try { /* ... */ }
        catch (Exception ex)
        {
            // pass the exception object: the log keeps type, message AND stack trace
            logger.LogError(ex, "Cancelling order {OrderId} failed", orderId);
            throw;   // rethrow with the original stack trace (not "throw ex;")
        }
    }
}
ToolUse it for
IDE debugger (VS, Rider, VS Code + C# Dev Kit)Stepping through logic, inspecting variables, breaking on exceptions
Debug.WriteLine / Debug.AssertDevelopment-only checks and traces; compiled out of Release builds
ILogger (Serilog, OpenTelemetry exporters)Structured logs from running services
Nullable warnings and Roslyn analyzersCatching bugs at build time; dotnet format applies fixes
dotnet-counters, dotnet-traceLive CPU, GC and thread-pool metrics, and traces of a running process
dotnet-dumpSnapshot a hung or crashing process and inspect its threads and heap later
A debugging routine that works
Reproduce it reliably first (ideally as a failing unit test). Read the full error, the first line and your first frame. State a guess, then prove or disprove it with one breakpoint or one log line — do not change five things at once. When it is fixed, keep the test so the bug cannot quietly come back.
07

Index of the 15 errors

Bookmark this table. Most beginner errors in C# are one of these fifteen.
#ErrorWhenUsual causeUsual fix
1CS0103 name does not existcompileOut of scope, typo, wrong caseDeclare in the outer scope; fix spelling
2CS0246 type not foundcompileMissing using or packageAdd the using / dotnet add package
3CS1002 ; expectedcompileMissing semicolonAdd it at the reported column
4CS1061 no definition for membercompileMisspelt member, wrong typeUse IntelliSense to find the real name
5CS0120 object reference requiredcompileInstance member called on the classCreate an object, or make it static
6CS0029 cannot convert string to intcompileText used as a numberint.TryParse
7CS0266 cannot convert double to intcompileNarrowing conversionKeep the double, or cast / round deliberately
8CS0019 operator cannot be appliedcompileComparing or combining mismatched typesConvert one side first
9CS0165 unassigned local variablecompileNot assigned on every pathInitialise at declaration
10CS0161 not all code paths returncompileMissing final return / elseReturn or throw on every path
11NullReferenceExceptionruntimeMember access on nullHeed CS8602; use ?., ??, checks
12IndexOutOfRangeExceptionruntimeIndex < 0 or >= Length (e.g. empty args)Check Length first
13InvalidCastExceptionruntimeCast to a type the object is notPattern matching (is, switch), parse text
14KeyNotFoundExceptionruntimeDictionary [key] on a missing keyTryGetValue
15FormatExceptionruntimeParsing text in an unexpected formatTryParse with NumberStyles and a culture
Compile error
A problem Roslyn finds while building (CSxxxx); the program does not run at all.
Warning
A diagnostic that does not stop the build but usually points at a real bug (e.g. CS8602).
Runtime exception
An error object thrown while the program runs; unhandled, it ends the process with a stack trace.
Stack trace
The chain of method calls active when an exception was thrown: throw site at the top, Main at the bottom.
Inner exception
The original exception wrapped inside another one, shown after --->.
Nullable reference types
The compiler feature that tracks string versus string? and warns before a null can be dereferenced.
Breakpoint
A marker that pauses execution in the debugger before a line runs.
Structured logging
Logging with named placeholders so each value is stored as a searchable field.
Quick check

A stack trace lists four "at" lines. Which one shows where the exception was actually thrown?

Quick check

Your code compiles, but you see warning CS8602: Dereference of a possibly null reference. What is the best response?

Frequently asked questions

How do I read a C# stack trace?
Start with the first line: the exception type and message. The first "at" line under it is where the exception was thrown; the bottom line is your entry point, Main. Read from the bottom up to follow the calls, then find the first frame in your own code — that is usually where the fix belongs. A line starting with ---> introduces the inner exception, the original cause.
What causes NullReferenceException in C# and how do I prevent it?
It happens when you access a member on a variable that is null, such as an unset property, a missing lookup result, or an uninitialised field. Keep nullable reference types enabled and treat CS8602/CS8600 warnings as bugs, use ?. and ?? where null is legitimate, and require values through constructors where it is not.
What is the difference between Debug.WriteLine and Console.WriteLine?
Console.WriteLine writes to the program's standard output, visible to users. Debug.WriteLine writes to trace listeners such as the IDE's Output window and is removed from Release builds, so it is only for development-time tracing. For production diagnostics, use ILogger.

Finish the C# handbook, then get hired

Sit the exam for your certificate, run your resume through the ATS checker, and see the jobs that ask for exactly this.

Check my resume
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.