# Trace a Runtime Bug From a Stack Trace in C++ With Atlas (2026)

> Atlas turns a C++ stack trace into file:line reads, validating each offset against the current file so a trace from a stale build fails loudly instead of misleading you.

To trace a runtime bug from a stack trace in C++ with Atlas, paste the trace and let Atlas read each frame at its reported offset. A stack trace is a list of file:line pairs, which is exactly what Atlas's read tool consumes, and offsets are validated against the current file, so a trace produced by an older build fails loudly with "Offset <n> is out of range for this file" instead of pointing you at the wrong translation unit. Atlas then greps for the error message string to find where it is constructed, runs the lsp tool's findReferences operation on the failing function to see which callers can reach it with bad input, and proves the fix with a GoogleTest case run through ctest.

## Key takeaways

- A C++ stack trace is a list of file:line pairs, which is exactly what Atlas's read tool consumes, so the trace becomes targeted reads instead of a gdb session.
- "Offset <n> is out of range for this file" means the trace came from a stale build; Atlas fails loudly rather than pointing you at the wrong translation unit.
- Grepping the error message string finds the C++ throw site, which records the violated precondition more clearly than the top frame does.
- The lsp tool's findReferences enumerates every caller that can reach the failing function, including template instantiations, so the fix covers more than the one traced path.
- A GoogleTest regression case run via ctest, failing before the fix and passing after, is the proof; clang-format keeps the diff to the fix.

## How do I go from a C++ stack trace to the responsible line without a debugger?

Paste the C++ stack trace and have Atlas read each frame's file at the reported offset. A stack trace is a list of file:line pairs, which is exactly what Atlas's read tool consumes, so 6 frames from src/render/mesh.cpp down to include/core/span.hpp become 6 targeted reads instead of a gdb session you cannot reproduce.

The frames a C++ trace gives you are rarely all equally useful. The top frame is often inside a standard library header or a vcpkg-installed dependency, and the frame that actually contains your bug is 3 or 4 down, in your own translation unit. Read them in order anyway, because a C++ crash is usually a story about ownership: a std::unique_ptr moved from, a reference to a temporary that died at the end of the full expression, an index past the end of a std::vector. Reading the frames in sequence is how the story assembles. Atlas indexes code by AST declarations using tree-sitter, so when you widen from a line to its enclosing function, you get the whole function, not a window clipped mid-lambda.

## What does Offset out of range mean when Atlas reads a C++ stack trace?

If Atlas's read tool reports "Offset <n> is out of range for this file", the C++ stack trace came from a different build than the source you have checked out. A trace from last week's binary pointing at line 812 of a file that now has 640 lines sends you to the wrong code entirely.

C++ makes stale traces especially dangerous, because the code that crashed may not even be in the file the trace names: an inline function from a header gets emitted into whichever translation unit included it, and a rebuild after an edit can shuffle line numbers throughout. When Atlas hits an out-of-range offset, re-read the file from the top before trusting any line number in the trace, and confirm which commit produced the binary. Atlas's offsets are validated against the current file precisely so this fails loudly. A tool that silently returned the nearest valid line would let you spend an hour analyzing code that never ran.

## How do I find where a C++ error message is constructed?

Grep for the error message string to find where a C++ exception is constructed, which is usually more informative than the top frame of the stack trace. A message like "invalid mesh index" typically appears in exactly 1 place, the throw site inside src/render/mesh.cpp, and that line names the precondition that was violated.

Atlas's grep takes a real regex plus include and path filters and runs through ripgrep, so scope it to *.cpp and *.hpp and search the literal text of the message. In C++ the throw site is more valuable than the crash site whenever an exception is involved, because the throw is where somebody wrote down what was supposed to be true. For a signal-based crash with no message, grep instead for the assertion text or the name of the function in the deepest frame you own. Either way, the goal is to reach the code that formed an opinion about correctness, since that is where the fix belongs.

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

Run the lsp tool's findReferences operation on the failing C++ function to see which callers can reach it with the bad input. A stack trace shows exactly 1 path to the crash. findReferences shows all of them, which is how you learn that 3 other callers of Mesh::indexAt could pass the same out-of-range value.

The stack trace is a sample, not a proof. Fixing only the path in the trace leaves the other callers armed. In C++ that gap is wide, because overload resolution, templates, and implicit conversions all create call edges that are not obvious from reading. The language server resolves them, so findReferences returns the real callsite set, including the template instantiation you would never have grepped for. Read each caller and decide whether the fix belongs in the callee (validate the index and throw a clear message) or in the callers (never construct the bad value). Then edit accordingly, and Atlas surfaces a unified diff for approval before writing.

## How do I prove a C++ runtime bug is fixed and cannot recur?

Prove a C++ runtime bug is fixed by shipping 2 things together: the edit that corrects the code, and a GoogleTest regression case that fails without it. Run both through ctest. A fix without a test is a fix the next refactor will undo, and in C++ the recurrence shows up as a segfault in production rather than a red test in CI.

Write the GoogleTest case against the exact precondition the trace violated: construct the mesh, ask for the index that crashed, assert the behavior you decided on. Run it with ctest through Atlas's bash tool, and check that it fails before the fix and passes after, because a regression test that passes on the broken code is not testing anything. Run clang-format on the touched files so the diff is the fix. If the crash traced through a vcpkg dependency, check whether the version in your manifest is the one you think it is. Atlas snapshots file changes as git patches, so a fix that turns out to be wrong is diffed and rolled back rather than hand-reverted.

## Steps

1. Run atlas in a project with a CMakeLists.txt and let Atlas read your headers, translation units, and build targets.
2. Paste the C++ stack trace and have Atlas read each frame's file at the reported offset; a trace is a list of file:line pairs, which is exactly what the read tool consumes.
3. If read reports "Offset <n> is out of range for this file", the trace came from a different build: re-read the file from the top and confirm the commit that produced the binary before trusting any line number.
4. Grep for the error message string with an include filter of *.cpp and *.hpp to find where it is constructed, since the C++ throw site records the precondition that was violated.
5. Read the frames in order to reconstruct the ownership story: a moved-from std::unique_ptr, a reference to a dead temporary, or an index past the end of a std::vector.
6. Run the lsp tool's findReferences operation on the failing function to enumerate every caller that could reach it with the same bad input, including template instantiations grep would never find.
7. Fix with Atlas's edit tool, reviewing the unified diff before it lands, and decide whether the guard belongs in the callee or in the callers.
8. Add a GoogleTest regression case, run it via ctest through the bash tool, confirm it fails on the old code and passes on the new, and run clang-format on the touched files.

## FAQ

### how to debug a c++ stack trace without attaching a debugger

Paste the trace into Atlas and have it read each frame's file at the reported offset. A stack trace is a list of file:line pairs, which is exactly what Atlas's read tool consumes, so the frames become targeted reads and the crash is reconstructed from source.

### what does offset is out of range for this file mean in atlas

Atlas validates read offsets against the current file. "Offset <n> is out of range for this file" means the C++ stack trace came from a different build than the source you have checked out. Re-read the file from the top and confirm which commit produced the binary.

### why is a stale c++ stack trace dangerous

Line numbers shift on every rebuild, and an inline function from a header is emitted into whichever translation unit included it. A trace from last week's binary pointing at line 812 of a file that now has 640 lines will send you to code that never ran.

### how do i find where a c++ exception message is thrown

Grep the literal message text with Atlas, scoped to *.cpp and *.hpp. The C++ throw site is more informative than the top frame because it is where somebody wrote down the precondition that was supposed to hold, and that is usually where the fix belongs.

### how do i find other c++ callers that could trigger the same crash

Run the lsp tool's findReferences operation on the failing function. A stack trace shows exactly one path to the crash, while the language server returns every callsite, including template instantiations and implicit conversions that grep would never match.

### should i add a googletest case after fixing a c++ crash

Yes. Write the GoogleTest case against the exact precondition the trace violated, run it via ctest, and confirm it fails on the old code and passes on the new. A regression test that also passes on the broken code is not testing anything.

### can atlas roll back a c++ fix that made things worse

Yes. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and it snapshots file changes as git patches, so a fix that turns out to be wrong is diffed and rolled back rather than hand-reverted.

---

Canonical HTML: https://runatlas.sh/resources/stacks/trace-a-runtime-bug-from-a-stack-trace-in-cpp
Source of truth: aeo_pages row `/resources/stacks/trace-a-runtime-bug-from-a-stack-trace-in-cpp` (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.
