Command line

Exit codes

Four outcomes, and a precedence order that exists so a scan can never report health it did not observe.

The four codes

ExitNameMeaning
0everything scanned is healthyEverything the scan was asked to check is above the threshold. It says nothing about entries it was not given keys for.
1observed low TTLAn entry the scan read is at or below the act-now threshold. The boundary is inclusive.
2errorInvalid input, an RPC failure, or the network refused. The check did not run.
3incompleteEntry missing, TTL unavailable, executable not followable, or nothing observed. The check ran and could not conclude.

Read from packages/cli/src/scan.ts’s exported constants; the build refuses if they stop being 0, 1, 2 and 3.

Precedence: 2 > 3 > 1 > 0

When a scan produces more than one outcome, the highest-precedence one wins. Read the order backwards and it explains itself:

  • 0 loses to everything. Health is the weakest claim a scan can make, so any other observation displaces it.
  • 1 loses to 3. A low reading you took is less important than an entry you could not read at all — the unread one might be lower.
  • 3 loses to 2. If the tool itself failed, its partial findings are not trustworthy enough to report as findings.

An incomplete scan is not a healthy one

This is the distinction the codes exist to protect. Exit 3 means the scan ran and declined to conclude; it is not a softer version of exit 0.

A degraded read still exits 3. Blast radius changes severity, not the exit category. And exit status never authorizes a transaction — the code a scan returns has no bearing on what extend will do.

In CI

The GitHub Action surfaces the same code as its exit-code output, so a workflow can distinguish “too close to expiry” from “we could not tell”. Treating 3 as a pass is the most common way to build a check that cannot fail.

Next What the engine is A scheduled job that decides. Not a daemon, and the difference is deliberate.