Stacks

Trace a runtime bug from a stack trace in Scala with Atlas (2026)

Updated 9 min read

In Scala, Atlas traces a runtime bug by treating the stack trace as what it literally is: a list of file:line pairs, which is exactly what Atlas's read tool consumes. Atlas reads each frame at its reported offset in your src/main/scala tree, greps for the error message string to find where the exception is constructed, uses the lsp tool's findReferences operation to see which callers can reach the failing method with bad input, then fixes with edit and locks the behavior in with a ScalaTest case run through sbt test. No debugger has to be attached to the JVM, and no sbt console session is needed.

How does Atlas trace a runtime bug from a Scala stack trace?

Atlas turns a Scala stack trace into a fix in 5 documented steps: read each frame at its reported offset, re-read the file if the offset is out of range, grep for the error message string, run the lsp tool's findReferences on the failing method, then edit and add a ScalaTest case, all inside a project rooted at build.sbt.

A Scala stack trace from a production JVM gives you frames like com.acme.billing.InvoiceService$.total(InvoiceService.scala:87) and a chain of $anonfun frames from the collection combinators. Atlas reads InvoiceService.scala at offset 87 rather than guessing, because Atlas's read tool takes a file and an offset directly. The frames Scala shows you are the ones that were on the stack, not the ones that put the bad value there, so Atlas follows up with grep for the error message string and with the lsp tool's findReferences operation on the failing method. The four Atlas tools this workflow uses are read, grep, lsp, and edit, and they map cleanly onto the four questions a Scala runtime bug asks: which line, which message, which caller, which change.

What does it mean when Atlas says the offset is out of range for this file?

When Atlas's read tool reports Offset <n> is out of range for this file, the Scala stack trace came from a different build than the source on disk. Atlas fails loudly instead of pointing at the wrong code, and the correct move in 2026 is to re-read the file from the top before trusting any line number.

Stack traces outlive builds. A trace captured from a jar assembled three sbt releases ago will still name InvoiceService.scala:87, but line 87 in your working tree may now be a blank line inside a different case class. Atlas validates the reported offset against the current file, so a stale trace produces an explicit Offset out of range error rather than a confident answer about the wrong code. When that happens, have Atlas read the file from the top, find the method the frame names, and work from the symbol rather than the number. Scala makes this failure especially common because the compiler generates $anonfun and $lzycompute frames whose line numbers shift whenever anything above them in the file moves.

Why grep for the error message instead of reading the top frame?

Atlas greps for the error message string because that finds where the exception is constructed, which is usually more informative than the top frame of a Scala stack trace. A require failed message or a MatchError text lives in exactly 1 place in src/main/scala, and grepping that literal lands there directly.

The top frame of a Scala trace is often a library method or a generated $anonfun, several layers away from your intent. The message text, by contrast, is a string you wrote. Atlas greps for it across the sbt module tree, including src/main/scala and src/test/scala, and lands on the require, the throw, or the sealed-trait match that produced it. Scala idioms make this pay off: an unmatched case in a pattern match throws a MatchError naming the offending value, and a failed require in a case class constructor names the predicate. Atlas searches code with hybrid semantic and keyword retrieval fused by reciprocal rank fusion, so when the exact literal is interpolated and grep alone would miss it, semantic retrieval still surfaces the construction site.

How do you find which callers can reach the failing Scala method?

Atlas runs the lsp tool's findReferences operation on the failing Scala method, step 4 of the documented trace workflow, to enumerate every caller that can reach it with bad input. Metals answers from the real symbol graph, so implicit conversions and trait mixins are included, which a grep on the method name would miss.

In Scala, a method can be reached through a trait it is defined in, through an implicit class extension, or through a for-comprehension that desugars into flatMap and map. Grepping the method name will not find those paths. The lsp tool's findReferences operation asks the language server for the real reference set, so Atlas sees every callsite that could have supplied the value that blew up. From there Atlas reads the two or three callers where the input is constructed, usually an Option unwrapped with .get or a String parsed without a Try, and the origin of the bad value becomes obvious. Atlas indexes code by AST declarations using tree-sitter, not blind line windows, so the candidates it brings back are whole declarations rather than arbitrary chunks.

How does Atlas review the fix before it touches build.sbt or your Scala sources?

Atlas computes a unified diff for every file edit and surfaces it for approval before writing, so no change to a .scala source or to build.sbt lands without you seeing it. Every Atlas tool call is gated against 3 rule types, allow, ask, and deny, which covers sbt test runs as well as edits.

Review in Atlas is structural, not advisory. Before Atlas edits a Scala source file it shows the unified diff and waits. Before Atlas runs sbt test it checks the command against your allow, ask, and deny rules. Atlas snapshots file changes as git patches, so an edit that turned out to be wrong can be diffed and rolled back rather than manually unpicked. For a trace-driven fix this matters because the tempting fix is often the wrong one: adding a null guard where the real defect is an Option that should never have been empty. Seeing the diff, then running scalafmt and sbt test, keeps the fix honest. Atlas also drafts a plan in a read-only plan agent and asks before switching to a build agent, so the investigation phase cannot accidentally write.

How do you keep a Scala stack trace from recurring silently?

Atlas adds a ScalaTest case that reproduces the trace and runs it with sbt test, because a regression test is the only thing that makes the bug fail loudly next time. Atlas writes the spec into src/test/scala alongside the fix, so both land in 1 reviewable change.

A Scala runtime bug that produced a stack trace once can produce it again after a refactor, and the next occurrence may be in a nightly job nobody watches. After the edit, have Atlas add a ScalaTest case in src/test/scala that feeds the exact input from the trace and asserts the correct result rather than the exception. Run it with sbt test. Format with scalafmt so the new spec matches the .scalafmt.conf your repo already enforces, and let sbt pull any test dependency the spec needs. Atlas reads git branches, status, and diffs, and can stage and create commits on your behalf, so the fix and its ScalaTest case go into one commit that explains itself.

Step by step

  1. 01Run atlas in a project with a build.sbt so Atlas can index your traits, implicits, and sbt modules.
  2. 02Paste the Scala stack trace into Atlas and have it read each frame's file at the reported offset with the read tool.
  3. 03If 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 before trusting any line number.
  4. 04Grep for the error message string, the MatchError text or the require message, to find where the exception is constructed in src/main/scala.
  5. 05Use the lsp tool's findReferences operation on the failing Scala method to see which callers can reach it with the bad input, including implicit and trait-mixin paths.
  6. 06Fix the production code with edit and review the unified diff Atlas surfaces before it writes.
  7. 07Add a ScalaTest case in src/test/scala that reproduces the trace, and run it with sbt test.
  8. 08Run scalafmt so the new spec matches .scalafmt.conf, then re-run sbt test on the whole module before committing.

Frequently asked questions

how to debug a scala stack trace without a debugger
Feed the stack trace to Atlas. Atlas reads each frame's file at the reported offset with its read tool, greps for the error message string to find where the exception is constructed, and uses the lsp tool's findReferences operation to identify callers. No JVM debugger has to be attached, and no remote debug port has to be opened.
why are scala stack trace line numbers wrong
The trace usually came from a different build than the source on disk. Atlas validates the reported offset against the current file, so read reports Offset <n> is out of range for this file rather than showing you the wrong code. Re-read the file from the top and work from the method name in the frame, not the number.
what do $anonfun frames mean in a scala stack trace
Scala's compiler generates $anonfun frames for anonymous functions passed to combinators like map and flatMap, so they appear between your methods in a trace. They rarely name the intent behind the failure, which is why Atlas greps for the error message string to find the construction site instead of stopping at the top frame.
how do I find every caller of a scala method
Use the lsp tool's findReferences operation through Atlas. The language server returns the real reference set, so callers reached through trait mixins, implicit classes, and desugared for-comprehensions are included. Grepping the method name in src/main/scala misses all three of those paths.
can atlas write a scalatest regression test for a bug I just fixed
Yes. Atlas writes the spec into src/test/scala, shows you the unified diff for approval before writing, and runs it with sbt test. Run scalafmt afterward so the new file matches the .scalafmt.conf your repo already enforces.
does atlas run sbt commands automatically
Only within your rules. Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, so an sbt test invocation is checked like any other command. You decide whether sbt runs are allowed, prompted, or denied.
how do I undo a fix atlas made to a scala file
Atlas snapshots file changes as git patches, so an edit to a Scala source file can be diffed and rolled back rather than manually unpicked. Atlas also reads git branches, status, and diffs, so you can see exactly what changed before deciding.
what do I need to run atlas on a scala project
Run atlas in a project with a build.sbt. Atlas reads your traits, implicits, and sbt modules, and from there it can refactor to typeclasses or add ScalaTest cases, showing you the diff for review before anything is written.

Try Atlas in your terminal

The terminal-native AI coding agent. Free core, single binary.

Install Atlas

Related guides

Trace a Runtime Bug from a Stack Trace with Atlas in 2026

How to trace a runtime bug from a stack trace with Atlas in 2026: read each frame at its offset, grep for the error string, and use the lsp tool to find callers.

Atlas for Scala in 2026

Atlas is a terminal-native AI coding agent for Scala in 2026. Run it in a project with a build.sbt, let it read your traits and implicits, and approve every diff.

Write Unit Tests for Untested Code in Scala with Atlas (2026)

How to add ScalaTest coverage to an untested Scala module with Atlas in 2026: enumerate symbols with lsp documentSymbol, copy repo conventions, then run sbt test.

Rename a symbol across the repo in Scala with Atlas in 2026

Refactor Scala code with Atlas in 2026. Safely rename functions, classes, or constants across your entire sbt project, ensuring all references, including comments and strings, are updated accurately.

Refactor a Legacy Scala Module with Atlas in 2026

Streamline legacy Scala modules in 2026 with Atlas. Safely refactor code, ensure no behavior changes, and maintain caller compatibility using sbt, ScalaTest, and scalafmt.

Diagnose a hanging or long-running command in Scala with Atlas (2026)

Is your sbt build slow or blocked on stdin? Atlas races every command against a timeout and tells you which. A 2026 guide for Scala teams using sbt and scalafmt.

Debug a Single Failing Test in Scala with Atlas (2026)

How Atlas debugs one failing Scala test in 2026: run it isolated with sbt, walk the call path with the lsp tool, and fix the code, not the assertion.

Extract a shared helper from duplicated code in Scala with Atlas in 2026

Refactor Scala code efficiently in 2026 by extracting shared helpers from duplicated logic using Atlas. Leverage semantic search, `sbt test`, and `scalafmt` for safe, reviewable changes.

Browse this resource hub