{"record":{"id":"969db93d9748b4cc","repo":"n8n-io/n8n","slug":"eval-this-evalname-cannot-use-both-check","errorCode":null,"errorMessage":"Eval \"${this.evalName}\": cannot use both .check() and .judge()","messagePattern":"Eval \"(.+?)\": cannot use both \\.check\\(\\) and \\.judge\\(\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/sdk/eval.ts","lineNumber":87,"sourceCode":"\tmodel(modelId: string): this {\n\t\t// TODO: support full model config\n\t\tthis.modelId = modelId;\n\t\treturn this;\n\t}\n\n\t/** Declare a credential for the judge model. */\n\tcredential(name: string): this {\n\t\tthis.credentialName = name;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a deterministic check function.\n\t * Mutually exclusive with `.judge()`.\n\t */\n\tcheck(fn: CheckFn): this {\n\t\tif (this.judgeFn) {\n\t\t\tthrow new Error(`Eval \"${this.evalName}\": cannot use both .check() and .judge()`);\n\t\t}\n\t\tthis.checkFn = fn;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set an LLM-as-judge handler. Requires `.model()` and `.credential()`.\n\t * The handler receives `{ input, output, expected, llm }` where `llm`\n\t * is a callable function bound to the judge model.\n\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}","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/sdk/eval.ts#L69-L105","documentation":"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.","triggerScenarios":"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.","commonSituations":"Refactoring an eval from judge to check mode without rebuilding; copy-pasting builder chains; conditional logic that calls both methods based on a flag.","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."],"exampleFix":"// before\nconst e = new Eval('x').judge(judgeFn).check(checkFn);\n// after\nconst eJudge = new Eval('x').judge(judgeFn);\nconst eCheck = new Eval('x-check').check(checkFn);","handlingStrategy":"validation","validationCode":"function assertCheckAllowed(evalInstance: { judgeFn?: unknown }) {\n  if (evalInstance.judgeFn) {\n    throw new Error('Eval already configured with .judge(); refuse .check()');\n  }\n}","typeGuard":"function hasJudge(e: { judgeFn?: unknown; checkFn?: unknown }): boolean {\n  return typeof e.judgeFn === 'function';\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["eval","builder","mutually-exclusive"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}