Summary
An eval that never drives an agent turn — e.g. one that calls t.target.dispatchSchedule(...) and then asserts database state with t.check — completes with result.output === null (per the documented derivation: "the final turn's structured data when present, otherwise its last assistant message, or null when none was produced").
When the Braintrust reporter is active, BraintrustReporter.onEvalComplete forwards that value verbatim:
this.#n.log({ id: e.id, input: ..., output: e.result.output, ... })
braintrust's validateAndSanitizeExperimentLogFullArgs rejects null and undefined alike (isEmpty(event.output) → throw new Error("output must be specified")), and the throw escapes onEvalComplete, killing the entire eve eval run — remaining evals never execute and no artifacts are written, even though the crashing eval itself passed all its gates.
Repro
- Any eval whose
test performs no t.target message turn (schedule-dispatch assertions over DB state are the natural case).
- Run
eve eval <that-eval> with BRAINTRUST_API_KEY set.
- The eval passes (
✓ gates n/n), then the run exits 1 with output must be specified.
Observed on eve 0.27.12 and 0.27.13 (reporter dist byte-identical across the two); braintrust 3.9.0.
Expected
A no-turn eval is a legitimate shape (the eval API's own output doc anticipates null). The reporter should sanitize before logging — e.g. output: e.result.output ?? "" or omit/stub the field — and a reporter throw in onEvalComplete arguably shouldn't abort the run and drop the remaining evals + artifacts either.
Workaround we're using
Split invocation: keyed eve eval for turn-producing evals (posts the experiment), a second un-keyed run for the no-turn evals (console gates only). Retires when this is fixed.
Summary
An eval that never drives an agent turn — e.g. one that calls
t.target.dispatchSchedule(...)and then asserts database state witht.check— completes withresult.output === null(per the documented derivation: "the final turn's structured data when present, otherwise its last assistant message, or null when none was produced").When the Braintrust reporter is active,
BraintrustReporter.onEvalCompleteforwards that value verbatim:braintrust'svalidateAndSanitizeExperimentLogFullArgsrejects null and undefined alike (isEmpty(event.output)→throw new Error("output must be specified")), and the throw escapesonEvalComplete, killing the entireeve evalrun — remaining evals never execute and no artifacts are written, even though the crashing eval itself passed all its gates.Repro
testperforms not.targetmessage turn (schedule-dispatch assertions over DB state are the natural case).eve eval <that-eval>withBRAINTRUST_API_KEYset.✓ gates n/n), then the run exits 1 withoutput must be specified.Observed on eve 0.27.12 and 0.27.13 (reporter dist byte-identical across the two); braintrust 3.9.0.
Expected
A no-turn eval is a legitimate shape (the eval API's own
outputdoc anticipatesnull). The reporter should sanitize before logging — e.g.output: e.result.output ?? ""or omit/stub the field — and a reporter throw inonEvalCompletearguably shouldn't abort the run and drop the remaining evals + artifacts either.Workaround we're using
Split invocation: keyed
eve evalfor turn-producing evals (posts the experiment), a second un-keyed run for the no-turn evals (console gates only). Retires when this is fixed.