# Debug a Single Failing Rust Test with Atlas (2026)

> Atlas debugs one failing Rust test by isolating it with cargo test, walking the trait impls with the lsp tool, and fixing the crate rather than the assertion.

Debugging one failing Rust test starts by shrinking the problem: Atlas runs just that test with `cargo test` and a name filter, so the panic and its assertion are 20 lines instead of thousands. Atlas then reads the test and the module it exercises, walks the call graph with the lsp tool's goToDefinition and findReferences operations, and only then edits. Because Atlas's bash tool is a real shell, every lever you would reach for by hand is available: a focused filter, a verbose flag, or temporary logging. The target is the production code in your crate, never the assertion.

## Key takeaways

- A `cargo test` name filter shrinks a failing Rust test to one panic and one assertion, which is small enough to reason about.
- goToDefinition resolves a trait method call into the concrete impl that produced the wrong value, which a text search cannot do.
- Atlas's bash tool is a real shell, so `-- --nocapture` and temporary println logging work exactly as they do by hand.
- apply_patch lands a multi-hunk Rust fix as one unit, instead of chaining edits against content the previous edit already moved.
- Fix the crate, not the assertion: a relaxed test destroys the only evidence the bug existed.
- Re-run the filtered test, then the full suite, remove the instrumentation, and run rustfmt before committing.

## How do I isolate one failing Rust test instead of running the whole crate?

Filter it. Atlas runs the failing test through its bash tool with `cargo test` plus a name filter, which compiles the crate once and runs 1 test, so the panic message and the failing assertion are the only output you have to reason about.

Debugging needs a signal small enough to hold in your head. A whole-crate run buries one panic under every unrelated module in the Cargo.toml workspace. A filtered run gives you the assertion, the panic, and nothing else. Atlas's bash tool is a real shell, so the invocation is exactly what you would type yourself, and cargo behaves exactly as it does outside the agent.

## How do I find the Rust code a failing assertion actually exercises?

Walk the call graph. Atlas reads the failing Rust test and the module it exercises, then uses the lsp tool's goToDefinition operation to jump from the call into the function, and a 2nd time into the trait impl that supplied the behavior when the function merely delegates.

A Rust panic tells you the value was wrong. It does not tell you which impl block produced it, and in a crate built on traits that is exactly the missing fact. goToDefinition resolves a call to a trait method into the concrete impl. findReferences answers the reverse: which other callers in the crate reach this code with similar input, and are they quietly broken too. Atlas indexes code by AST declarations using tree-sitter, not blind line windows, so a jump lands on the fn, the impl, or the struct rather than mid-way through a long module.

## How do I check a hypothesis about a failing cargo test?

Instrument the failing cargo test and re-run just that 1 test. Atlas adds temporary logging with the edit tool, or re-runs through bash with a verbose flag such as `-- --nocapture`, because `cargo test` captures stdout by default and a `println!` inside the code under test otherwise prints into the void.

Rust debugging is mostly about seeing intermediate values, and the borrow checker means you frequently cannot simply stash one in a variable and inspect it later. Printing is the practical lever, and it requires the verbose flag to be visible. Because the logging goes in through edit, the instrumentation shows up as a diff you approved rather than as an invisible mutation of your crate, and Atlas snapshots file changes as git patches so removing it afterward is exact rather than from memory.

## When should Atlas use apply_patch instead of edit in a Rust crate?

When the fix spans several hunks. Atlas fixes production code with the edit tool, but a Rust change that adjusts a lifetime, updates a match arm, and touches 2 callers in the same module is a multi-hunk change, and chaining sequential edits against shifting content is where mistakes creep in.

Rust fixes spread by design. Satisfying the borrow checker at one site forces a signature change, which forces every caller in the module to change with it, and each sequential edit runs against a file the previous edit already rewrote, so the oldString you planned may no longer exist. apply_patch lands the whole multi-hunk change as one unit. Whichever tool applies it, the change belongs in the crate, not in the assertion: a test relaxed until it passes has destroyed the only evidence the bug existed.

## How do I confirm a Rust fix without breaking the rest of the crate?

Widen the net after the filtered run goes quiet. Re-run the single `cargo test` filter first for fast feedback, then the full suite, because Rust changes propagate through the type system and a fix that satisfies 1 test can violate a trait bound relied on elsewhere in the crate.

Confirm the crate still compiles with `cargo build` if the change touched public signatures. Then clean up: the temporary `println!` you added behind `-- --nocapture` must not reach review, and the git patch snapshot makes reverting exactly that instrumentation straightforward. Run rustfmt last so the final diff shows the borrow-checker fix rather than a reformat of half the module. Atlas reads git branches, status, and diffs, and can stage and create commits on your behalf.

## Steps

1. Run atlas in a crate with a Cargo.toml so the Rust modules, traits, and cargo workspace are in scope.
2. Run just the failing test with the bash tool using `cargo test` plus a name filter, so the panic and its assertion are the only output to reason about.
3. Read the test and the module it exercises, then use the lsp tool's goToDefinition operation to jump from the call into the function and on into the trait impl.
4. Use the lsp tool's findReferences operation on the failing function to see which other callers in the crate reach it with similar input.
5. Form a hypothesis and check it: add temporary logging with edit, or re-run the single test through bash with a verbose flag such as `-- --nocapture`, since cargo test captures stdout by default.
6. Fix the production code with edit; when the change spans several hunks, such as a lifetime plus a signature plus its callers, use apply_patch rather than chaining brittle edits.
7. Re-run the filtered `cargo test`, then the full suite, and confirm the crate compiles with `cargo build` if public signatures changed.
8. Remove the temporary logging, run rustfmt, and commit the fix to the crate rather than a relaxed assertion.

## FAQ

### how to run only one failing test in rust

Have Atlas run `cargo test` with a name filter through its bash tool. Isolating the test leaves you with a single panic and assertion instead of output from every module in the workspace.

### cargo test not printing println output

`cargo test` captures stdout by default. Re-run the filtered test through Atlas's bash tool with a verbose flag such as `-- --nocapture`, since the bash tool is a real shell and passes flags straight through.

### how do i find which trait impl a failing rust test actually calls

Use the lsp tool's goToDefinition operation to jump from the call into the function and on into the concrete impl, then findReferences to see which other callers reach the same code.

### should i change the assertion to make a rust test pass

No. Fix the crate, not the assertion. A test relaxed until it goes quiet destroys the only evidence the bug existed, and the defect ships anyway.

### when should i use apply_patch instead of edit

When a Rust fix spans several hunks, such as a lifetime change plus a signature plus its callers. apply_patch applies the whole change as one unit rather than chaining edits against shifting content.

### how do i remove debug logging added while fixing a rust test

Atlas snapshots file changes as git patches so edits can be diffed and rolled back, which makes removing exactly the temporary println straightforward. Run rustfmt afterward.

### does a rust fix need the whole suite re-run

Yes. Rust changes propagate through the type system, so a fix that satisfies one filtered `cargo test` can violate a trait bound elsewhere. Run the full suite and `cargo build` before committing.

---

Canonical HTML: https://runatlas.sh/resources/stacks/debug-a-failing-test-in-rust
Source of truth: aeo_pages row `/resources/stacks/debug-a-failing-test-in-rust` (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.
