{"record":{"id":"6b35a47a4fd8b8e0","repo":"n8n-io/n8n","slug":"eval-this-evalname-requires-either-check-o","errorCode":null,"errorMessage":"Eval \"${this.evalName}\" requires either .check() or .judge()","messagePattern":"Eval \"(.+?)\" requires either \\.check\\(\\) or \\.judge\\(\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/sdk/eval.ts","lineNumber":115,"sourceCode":"\t * Mutually exclusive with `.check()`.\n\t */\n\tjudge(fn: JudgeHandlerFn): this {\n\t\tif (this.checkFn) {\n\t\t\tthrow new Error(`Eval \"${this.evalName}\": cannot use both .check() and .judge()`);\n\t\t}\n\t\tthis.judgeFn = fn;\n\t\treturn this;\n\t}\n\n\t/** The eval name. */\n\tget name(): string {\n\t\treturn this.evalName;\n\t}\n\n\t/** @internal Build the eval into a runnable form. */\n\tprotected build(): BuiltEval {\n\t\tif (!this.checkFn && !this.judgeFn) {\n\t\t\tthrow new Error(`Eval \"${this.evalName}\" requires either .check() or .judge()`);\n\t\t}\n\n\t\tif (this.judgeFn && !this.modelId) {\n\t\t\tthrow new Error(`Eval \"${this.evalName}\" uses .judge() but no .model() was set`);\n\t\t}\n\n\t\tconst name = this.evalName;\n\t\tconst desc = this.desc;\n\n\t\tif (this.checkFn) {\n\t\t\tconst checkFn = this.checkFn;\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\tdescription: desc,\n\t\t\t\tevalType: 'check' as const,\n\t\t\t\tmodelId: this.modelId ?? null,\n\t\t\t\tcredentialName: this.credentialName ?? null,\n\t\t\t\t_run: async (input: EvalInput) => await checkFn(input),","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/sdk/eval.ts#L97-L133","documentation":"Thrown by Eval.build() (invoked lazily on first .run()) when neither .check() nor .judge() was configured. An Eval must declare exactly one scoring strategy before it can execute.","triggerScenarios":"Constructing an Eval, optionally setting .description/.model/.credential, then calling .run(input) or passing it to a runner that triggers build — without ever calling .check() or .judge().","commonSituations":"Builder chain interrupted by an exception or early return; conditionally setting the scoring function behind a flag that resolved false; copy-paste that dropped the scoring line.","solutions":["Add .check(fn) for deterministic scoring or .judge(fn) plus .model(id) plus .credential(name) for LLM-as-judge.","Gate eval construction on the condition that selects a scoring mode so the builder always ends with one.","Assert in tests that new Eval(name) chains always terminate with .check or .judge."],"exampleFix":"// before\nconst e = new Eval('json').description('validates json');\nawait e.run({ input: '', output: '{}', expected: '' });\n// after\nconst e = new Eval('json').description('validates json').check(({ output }) => {\n  try { JSON.parse(output); return { score: 1, reasoning: 'ok' }; }\n  catch { return { score: 0, reasoning: 'bad' }; }\n});","handlingStrategy":"validation","validationCode":"function assertEvalReady(e: { checkFn?: unknown; judgeFn?: unknown }) {\n  if (!e.checkFn && !e.judgeFn) {\n    throw new Error('Eval has no scoring function; call .check() or .judge()');\n  }\n}","typeGuard":"function evalHasScorer(e: { checkFn?: unknown; judgeFn?: unknown }): boolean {\n  return typeof e.checkFn === 'function' || typeof e.judgeFn === 'function';\n}","tryCatchPattern":null,"preventionTips":["Always end an Eval chain with .check or (.model + .credential + .judge).","Add a test that builds each Eval to surface missing scorers at test time."],"tags":["eval","builder","validation"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}