A false negative, labelling a reachable vulnerability as not reachable, usually costs more, because it can leave a real hole open. A false positive, labelling an unreachable one as reachable, costs time and trust. Weigh them by the decision at stake, and design your process so false negatives get caught.
Defining the two errors
| Error | What the tool said | What was true | Typical cost |
|---|---|---|---|
| False positive | Reachable | Not reachable | Engineer time, alert fatigue |
| False negative | Not reachable | Reachable | Unpatched exposure, wrong audit statement |
Static call graphs are approximations that tend to include every real call plus some extra ones, so false positives are usually more common than false negatives, except where code uses reflection or dynamic loading, where edges can be missed.
Why the costs are not symmetric
A false positive is visible. Someone looks at the finding, fails to see a real path, and closes it. The cost is bounded by the review time.
A false negative is invisible. The finding is filed under "not reachable", it drops out of the queue, and nobody looks again until an incident or an audit question. The cost is unbounded in the worst case, and there may be a written statement, such as a VEX document, that says the product is not affected.
But false positives are not free
If a team sees a long list of "reachable" findings that mostly turn out to be wrong, they stop reading the list. That is how a real finding gets missed. So the aim is to keep false positives low enough that people still believe the output, without paying for it with silent false negatives.
A practical balance
- Use three states. Reachable, not reachable and unknown, so uncertainty is explicit.
- Set a cautious default for unknown. Treat it as open, not closed.
- Raise caution with the stakes. For a formal risk acceptance or a not-affected statement, require the call path evidence and a second reviewer.
- Sample the negatives. Each quarter, pick a handful of "not reachable" decisions and check them by hand or with a seeded test.
- Pair with other signals. Known exploitation, exposure and severity should still raise priority for a finding the tool called unreachable.
Measuring your own rates
You can estimate your false negative risk with a seeded test. Add a small, harmless function that mimics a vulnerable one, call it from a known path, and check that the tool reports it. Add another that is never called and check that the tool does not. A tool that misses your seeded reachable case has shown you its blind spot cheaply.
Safeguard includes reachability analysis alongside SBOM and dependency scanning, and you can run a free scan at gold.safeguard.sh/scan to see the output on your own repository.
Quick check
Which error should I tune against first?
Tune against false negatives for high-stakes decisions, and keep false positives low enough that the team still trusts the list.
Can I measure the error rate without a vendor?
Yes. Use seeded functions with known reachability and sample past decisions.
Sources
Last reviewed: 19 October 2026.