Skip to content

Diagnostics

Every run returns a DiagnosticReport alongside the snapshot. Read the report when a run came back smaller than expected; it tells you why without poking at engine internals.

DiagnosticReport

rust
pub struct DiagnosticReport {
    pub stall_reason:       Option<RuntimeDiagnostic>,
    pub unmet_expectations: Vec<RuntimeDiagnostic>,
}

pub struct RuntimeDiagnostic {
    pub message: String,
    pub line:    Option<u32>,   // 0-based source line, when resolvable
}

stall_reason

Present only when a successful run was cut short — it flags that the snapshot may be incomplete. A clean run leaves it absent: an empty report is the "ran to completion" signal (forage test keys its exit code off that). Hard failures don't land here — a failed HTTP request, a visit with no recorded capture, or an expression that threw end the run with an error, not a stall.

The message is free-form, not a fixed tag. The cases today:

messageWhen
visit '<name>' hit maxIterations (<n>) before settling; the capture may be incompleteA visit's scroll / click loop ran its maxIterations cap without the page going quiet.
visit '<name>' ran out of time before settling; the capture may be incompleteA visit exceeded its time budget without the page going quiet.
output capped at sample limit <n> (<m> available); this is a sampled subsetsample_limit trimmed a top-level for, so the snapshot is a deliberate subset.

The first stall wins: a run with several truncation points reports the earliest.

unmet_expectations

One RuntimeDiagnostic per failing expect { … } clause, its message a pre-rendered description and its line the clause's source line. See the expectations page for syntax and rendering.

records.where(typeName == "Product").count >= 500 (got 247)

Reading a report

forage run prints the report's non-empty sections after the snapshot:

  • Unmet expectations → the recipe ran but produced less than the expect clauses demand. When a visit is involved, this usually means it didn't reach all the data: the page needs more scroll / click iterations before it settles, or a matched("…") pattern points at the wrong endpoint.
  • Stall reason → the run finished but was cut short: a visit gave up before settling, or sample_limit trimmed the output. The message carries the specifics. (An outright failure — bad HTTP status, missing replay capture, a throwing expression — surfaces as a run error instead, not in the report.)

In Studio, the Diagnostic tab renders each section as a list; click a row to jump to the relevant span in the editor.

Persisting reports

The diagnostic is part of the Snapshot that forage test diffs against _snapshots/<recipe>.json. Re-reading a snapshot lets you diff today's report against last week's without rerunning the engine.