# Troubleshooting
Start with the run summary. It usually tells you whether the problem is the test
command, source discovery, runner attribution, or a measurement boundary.
```sh supercov
npx supercov runs latest
npx supercov runs latest scope
npx supercov runs latest runners
```
## `npx` cannot start Supercov
The first `npx supercov` invocation may need to reach the npm registry. Check
Node.js first:
```sh
node --version
npx supercov --version
```
Supercov requires Node.js 22 or newer. Registry, proxy, authentication, or
offline-cache errors happen before the Supercov CLI starts; resolve them as you
would for another npm package.
## No application source was found
Run the same command from the repository root. If first-party code lives in an
unusual directory, declare the source roots explicitly:
```sh supercov
SUPERCOV_SOURCE_ROOTS=src,app npx supercov -- npm test
```
Then inspect what Supercov included and excluded:
```sh supercov
npx supercov runs latest scope
```
Do not broaden the roots to dependencies or generated output merely to remove a
warning. The goal is an honest boundary around code the repository owns.
## The tests pass but coverage is missing
First check runner and source scope:
```sh supercov
npx supercov runs latest runners
npx supercov runs latest scope
npx supercov runs latest gaps
```
Some runners expose exact test boundaries; others provide only aggregate
coverage. Processes inside a container, VM, remote executor, or hidden launcher
may also sit beyond the instrumentation boundary. Supercov reports that limit
instead of assigning execution to a test that may not have caused it.
Compare the command with [Supported suites](supported-suites.md). If the runner
should be supported, preserve the summary and exact command when reporting the
problem.
## A Ruby file behaves differently while Supercov measures it
Supercov splices probe calls into Ruby sources in memory as they load, and a
file that cannot be compiled with them is measured through Ruby's `Coverage`
module alone rather than failing the run. To put a file on that path
deliberately, name a fragment of its path:
```sh supercov
SUPERCOV_RUBY_SKIP_PROBES=app/models/order.rb npx supercov -- bundle exec rspec
```
Its lines, methods and simple branches stay measured; everything that needs a
probe is declared as a measurement limit for that file. Please report the file,
since Supercov aims to instrument every Ruby source correctly.
## A test that reads your source fails under Supercov
A test that opens your source files and asserts on their text sees the probes,
because that is what instrumentation is. RxJava has one: `CheckCatchThrowIfFatal`
reads every `catch` block in the project and requires the first statement to be
`Exceptions.throwIfFatal`, and under Supercov the first statement is a probe. An
architecture or convention test that scans bytecode or source can fail the same
way.
Nothing is wrong with the measurement and nothing your code does has changed --
the run still records every test that ran -- but the command exited non-zero, so
its coverage is reported as diagnostic and cannot gate. Leave that one test out
of the command you give Supercov. Surefire excludes by name:
```sh supercov
npx supercov -- mvn test -Dtest='!CheckCatchThrowIfFatalTest'
```
Gradle's `--tests` only includes, so exclude it in your build file instead --
Supercov measures the copy, and your own build is unaffected by what it runs:
```groovy
test { filter { excludeTestsMatching "*CheckCatchThrowIfFatal*" } }
```
Either way your own build still runs the test, against your own sources.
## A run is marked stale
A stored run remains valid history, but it stops describing the current
workspace after relevant source, tests, dependencies, configuration, or
toolchain inputs change. Rerun the same complete command to create a current
baseline:
```sh supercov
npx supercov -- npm test
```
Use immutable run ids in review notes and automation. Use `latest` only when the
newest local run is the one you intend to inspect.
## `gaps` shows a measurement limit
A measurement limit is not an uncovered path. It means Supercov could not
establish a complete measurement boundary—for example, because source scope is
ambiguous, code creates source dynamically, or execution crossed an unsupported
boundary.
Read the reason in the summary, `scope`, or `gaps` output. Fix a configuration
problem when one is named. Otherwise stop the coverage loop and report the
limit; do not change application code or add a meaningless test to chase 100%.
One limit names a cause you can remove. `ruby-process-did-not-report` means a
Ruby process the run measured ended without reporting: it was killed with
`SIGKILL`, or left through `exit!`, before the exit where Ruby hands over its
coverage. Whatever that process observed since its last test boundary is gone,
and its lines are not shown as uncovered because nothing can say whether they
ran. Stop the process with `SIGTERM` instead, or wait for it to exit, and the
limit does not appear.
## Coverage is aggregate instead of per test
Aggregate coverage still shows which source ran, but Supercov cannot truthfully
say which individual test caused it. This is expected for Node runners without
an exact adapter and for background work without a reliable test identity.
Use the whole-run `gaps` and `file` views. Per-test queries become useful when
the runner exposes exact test and attempt boundaries.
## A second command says Supercov is busy
One project can publish or clean one coverage store at a time. Let the active
run finish before starting another Supercov command. If a process was
interrupted, the next command recovers its unpublished staging state; completed
runs remain intact.
## The first run is slow
The first run may include the npm download, browser or toolchain startup,
workspace creation, and an instrumented build. Repeated runs can reuse the
isolated build when source, dependencies, configuration, toolchain, and build
mode still match.
Inspect the recorded phases with:
```sh supercov
npx supercov runs latest
```
See [Speed and storage](performance.md) for practical ways to shorten a loop.
## Supercov is using too much disk space
Preview cleanup, then choose how much history to keep:
```sh supercov
npx supercov clean --dry-run
npx supercov clean --keep 20
npx supercov clean
```
The final command removes all stored runs and the isolated build cache. Cleanup
only removes marker-owned Supercov data.
## Ask for help
Open an issue in the [Supercov repository](https://github.com/supercorp-ai/supercov/issues)
with:
- `npx supercov --version`;
- the exact wrapped test command;
- the relevant summary, `scope`, and `runners` output; and
- a small reproduction when the repository can be shared.
Do not include secrets, private source, or raw evidence from a repository you
cannot share.
## "malformed evidence record(s) ... were excluded"
Application processes append coverage evidence themselves. When two of them
share one file — pool runners that restore several VMs from a single snapshot
run clones of the same server process, with the same process id — an append
can tear another's line. Supercov skips the unreadable line, counts it under
`CORRUPT_EVIDENCE_RECORDS`, and treats it as a blocking limitation so the run
never looks complete. Since 0.0.29 the background and execution-trace writers also detect a
clone sharing their file and move to a fresh one, so this should be rare; if it
persists, check whether something outside Supercov appends to
`.supercov/…/server/background/`.
## "Complete ... — N declared boundary(ies)"
A limitation is either blocking or declared. A blocking one means Supercov
could not measure something inside the denominator it claims — corrupt
evidence, a transport that never reported — and the run reads as
"Incomplete". A declared one marks a boundary of the denominator itself: a
Rust macro the compiler expands, a `const fn` body no runtime probe can
enter, a proc-macro crate whose code runs inside the compiler. Nothing inside
the measured denominator went unmeasured, so the run reads as complete within
those boundaries, and each boundary is reported at the line it covers with a
reason. `runs <run> file <path>` lists them per file; the `measurement`
object in `--json` output counts them under `declared`.