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

  1. Pick one mode per Eval — create a second Eval instance for the other strategy.
  2. Audit builder chains for any path that can reach both .check() and .judge().
  3. 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

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


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/969db93d9748b4cc. Report an issue: GitHub.