n8n-io/n8n · error · Error
Eval "${this.evalName}": cannot use both .check() and .judge
Error message
Eval "${this.evalName}": cannot use both .check() and .judge() What it means
Thrown by Eval.check() when .judge() has already been called on the same Eval builder. The builder enforces mutual exclusion between deterministic .check() and LLM-as-judge .judge() modes; only one scoring strategy is allowed per Eval.
Source
Thrown at packages/@n8n/agents/src/sdk/eval.ts:87
model(modelId: string): this {
// TODO: support full model config
this.modelId = modelId;
return this;
}
/** Declare a credential for the judge model. */
credential(name: string): this {
this.credentialName = name;
return this;
}
/**
* Set a deterministic check function.
* Mutually exclusive with `.judge()`.
*/
check(fn: CheckFn): this {
if (this.judgeFn) {
throw new Error(`Eval "${this.evalName}": cannot use both .check() and .judge()`);
}
this.checkFn = fn;
return this;
}
/**
* Set an LLM-as-judge handler. Requires `.model()` and `.credential()`.
* The handler receives `{ input, output, expected, llm }` where `llm`
* is a callable function bound to the judge model.
* Mutually exclusive with `.check()`.
*/
judge(fn: JudgeHandlerFn): this {
if (this.checkFn) {
throw new Error(`Eval "${this.evalName}": cannot use both .check() and .judge()`);
}
this.judgeFn = fn;
return this;
}View on GitHub (pinned to 5ac6606e81)
Solutions
- Pick one mode per Eval — create a second Eval instance for the other strategy.
- Audit builder chains for any path that can reach both .check() and .judge().
- Reset by constructing a fresh Eval rather than reusing a builder.
Example fix
// before
const e = new Eval('x').judge(judgeFn).check(checkFn);
// after
const eJudge = new Eval('x').judge(judgeFn);
const eCheck = new Eval('x-check').check(checkFn); Defensive patterns
Strategy: validation
Validate before calling
function assertCheckAllowed(evalInstance: { judgeFn?: unknown }) {
if (evalInstance.judgeFn) {
throw new Error('Eval already configured with .judge(); refuse .check()');
}
} Type guard
function hasJudge(e: { judgeFn?: unknown; checkFn?: unknown }): boolean {
return typeof e.judgeFn === 'function';
} Prevention
- Build a helper that always terminates an Eval chain in exactly one of .check or .judge.
- Static-check eval construction in tests by asserting exactly one of checkFn/judgeFn is set.
When it happens
Trigger: Calling .judge(fn) then .check(fn2) on the same Eval instance. The check fires before the new function is assigned, preserving the previously configured judge handler.
Common situations: Refactoring an eval from judge to check mode without rebuilding; copy-pasting builder chains; conditional logic that calls both methods based on a flag.
Related errors
- Eval "${this.evalName}" requires either .check() or .judge()
- Eval "${this.evalName}" uses .judge() but no .model() was se
- Tool "${this.name}" cannot use both approval (.requireApprov
- Guardrail "${this.name}" requires a type
- Guardrail "${this.name}" requires a strategy
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/969db93d9748b4cc.
Report an issue: GitHub.