# Trace a Runtime Bug from a Stack Trace in C# with Atlas (2026)

> Atlas reads each frame of a C# stack trace at its reported offset and validates the offset against the current file, so a stale .NET trace fails loudly instead of misleading you.

To trace a runtime bug from a stack trace in C# with Atlas, paste the trace and have Atlas read each frame's file at the reported offset. A .NET stack trace is a list of file and line pairs, which is exactly what Atlas's read tool consumes. Atlas reads each frame, greps for the exception message string to find where it is constructed, and uses the lsp tool's findReferences operation to see which callers can reach the failing method with the bad input. Offsets are validated against the current file, so a trace from an older dotnet build fails loudly instead of pointing at the wrong C# code. Atlas then fixes with edit and adds a regression test you run with xUnit via dotnet test.

## Key takeaways

- A C# stack trace is a list of file and line pairs, which is exactly what Atlas's read tool consumes.
- Offset n is out of range for this file means the .NET trace came from an older build, so re-read before trusting any line number.
- Grep for the exception message string: the throw site in a C# guard clause says more than the top frame.
- The lsp tool's findReferences operation crosses .csproj project references that a plain text search would never reach.
- Add an xUnit regression test and run it with dotnet test, so the trace cannot recur silently.
- Atlas computes a unified diff for every edit to a .cs file and surfaces it for approval before dotnet build ever runs.

## How does Atlas read a C# stack trace without a debugger attached?

Atlas treats a C# stack trace as what it is: a list of file and line pairs, which is exactly the input Atlas's read tool consumes. Paste the trace from your .NET service logs, and Atlas reads each of its 20 or 30 frames at the reported offset, reconstructing the path with no debugger attached.

Production rarely offers you a debugger. A C# exception in a running service leaves behind a stack trace with a method, a file, and a line for each frame, and that is enough. Atlas reads each frame at its offset with the read tool, top frame down through the callers .NET recorded. Where the trace goes quiet, in async continuations or across a library boundary from a NuGet package, Atlas fills the gap by grepping and by walking the symbol graph. Atlas indexes code by AST declarations using tree-sitter, not blind line windows, so the method it pulls back around line 214 of OrderService.cs is the real method declaration.

## What does it mean when Atlas says Offset is out of range for this file?

When Atlas's read tool reports Offset 214 is out of range for this file, the C# stack trace came from a different build than the source on disk. Atlas validates every offset against the current file, so a stale .NET trace fails loudly instead of confidently pointing at the wrong line.

The worst failure in stack trace debugging is a line number that resolves to plausible but unrelated code. Atlas refuses to do that. Read validates the requested offset against the file as it exists now, and when the trace's line number does not exist in the current file, Atlas reports Offset n is out of range for this file rather than clamping to something nearby. In C# this happens constantly: the trace came from a release build of the service that shipped two weeks ago, and Program.cs has moved since. When you see that message, re-read the file from the top before trusting any line number in the trace.

## How do I find where a C# exception message is actually constructed?

Atlas greps for the exception message string to find where it is constructed, which is usually more informative than frame 0 of a C# stack trace. The throw site in a validation guard tells you what invariant was violated, while the top frame only tells you where .NET noticed the failure.

The top frame of a C# trace is where the exception surfaced, not where the logic went wrong. Atlas greps for the literal message text across the solution, and the hit is typically a throw new ArgumentException or a guard clause in a service class, with the surrounding code stating the condition that was supposed to hold. That construction site carries the intent. Atlas's retrieval also fuses hybrid semantic and keyword search with reciprocal rank fusion, so a message assembled from an interpolated string, where no single literal matches, is still findable by meaning. Once the throw site is located, the question becomes which caller supplied the input that tripped it.

## How do I find which callers can reach a failing C# method with bad input?

Atlas uses the lsp tool's findReferences operation on the failing C# method to enumerate every callsite, then reads each one to see which can actually supply the bad input. In a .NET solution with 8 .csproj projects, findReferences crosses project references that a text search would miss.

A stack trace shows one path. findReferences shows all of them. Atlas runs the lsp tool's findReferences operation on the method that threw, and the language server returns every caller across the solution, including callers in other projects wired together through .csproj project references and callers reached through an interface. Atlas then reads those callsites and asks the only question that matters: which of them can pass the value that broke the guard. Frequently the answer is a controller or a background worker that never validated a NuGet-deserialized payload, and the responsible line is nowhere in the original trace at all.

## How do I make sure a C# stack trace bug cannot come back?

Atlas fixes the C# bug with the edit tool and adds 1 regression test you run with xUnit via dotnet test, so the trace cannot recur silently. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, so the fix is reviewed before dotnet build ever runs.

A fix without a test is a fix that reoccurs. Atlas applies the change with edit, showing you the unified diff of the .cs file for approval before anything is written, then adds an xUnit test that reproduces the bad input the trace exposed. Run it with dotnet test to prove the test fails against the old behavior and passes against the fix. Every Atlas tool call is permission-gated against allow, ask, and deny rules, so you can allow dotnet test and dotnet build while still approving each write. Run dotnet format afterward, separately, so style changes stay out of the bug fix diff. Atlas also snapshots file changes as git patches, so a wrong fix can be rolled back.

## Steps

1. Run atlas in the solution directory that holds your .csproj or .sln, and let Atlas read your namespaces, project references, and NuGet packages.
2. Paste the C# stack trace and have Atlas read each frame's .cs file at the reported offset with the read tool.
3. If read reports Offset n is out of range for this file, the trace came from a different .NET build; re-read the file from the top before trusting any line number.
4. Have Atlas grep for the exception message string to find where it is constructed, which is usually more informative than the top frame.
5. Use the lsp tool's findReferences operation on the failing method to enumerate every caller, including callers across .csproj project references, and read each one.
6. Identify which caller can supply the input that violated the guard, then fix the responsible C# code with the edit tool, reviewing the unified diff before it lands.
7. Add an xUnit regression test that reproduces the bad input, and run it with xUnit via dotnet test to prove it fails before the fix and passes after.
8. Run dotnet build to confirm the solution compiles, then run dotnet format separately so style changes stay out of the bug fix diff.

## FAQ

### how to debug a c# production stack trace without a debugger

Paste the trace into Atlas and have it read each frame's .cs file at the reported offset. Atlas greps for the exception message to find the throw site, then uses the lsp tool's findReferences operation to see which callers can reach the failing method with bad input.

### atlas says offset is out of range for this file c#

That message means the C# stack trace came from a different .NET build than the source on disk. Atlas validates every offset against the current file rather than clamping to a nearby line. Re-read the file from the top before trusting any line number in the trace.

### why is the top frame of a stack trace not the real bug

The top frame of a C# trace is where .NET noticed the failure, not where the logic went wrong. Grep for the exception message string to find where the exception is constructed, because the guard clause that threw it states the invariant that was violated.

### how do i find every caller of a c# method across projects

Use the lsp tool's findReferences operation through Atlas. The language server returns callers across the whole solution, including ones reached through .csproj project references and interfaces, which a plain text search over your namespaces would miss.

### how do i write a regression test for a c# runtime exception

Have Atlas add an xUnit test that feeds the bad input the stack trace exposed, and run it with xUnit via dotnet test. Confirm it fails against the old behavior and passes after the fix, so the trace cannot recur silently.

### does atlas edit my .cs files without asking

No. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and every tool call is permission-gated against allow, ask, and deny rules. You can allow dotnet build and dotnet test while still gating writes.

### how do i set up atlas in a dotnet solution

Run atlas in a solution with a .csproj or .sln, let Atlas read your namespaces, project references, and NuGet packages, then ask it to add xUnit tests or refactor async code, reviewing the diff before dotnet build.

---

Canonical HTML: https://runatlas.sh/resources/stacks/trace-a-runtime-bug-from-a-stack-trace-in-csharp
Source of truth: aeo_pages row `/resources/stacks/trace-a-runtime-bug-from-a-stack-trace-in-csharp` (segment: Stacks) (this file is generated from it, never hand-edited).
Licence: Atlas is proprietary with a free core. It is not open source and there is no public source repository.
