n8n-io/n8n · error · OperationalError

LLM doesn't support binding tools

Error message

LLM doesn't support binding tools

What it means

`createEvaluatorChain` builds a LangChain structured-output chain for an LLM-judge evaluator; before doing so it asserts that `llm.bindTools` exists. `bindTools` is the LangChain capability marker for tool-calling/structured-output models, so its absence means the chosen model cannot drive the judge schema. Despite the message text, the chain uses `withStructuredOutput`, not `bindTools` directly — the check is a proxy for 'this model supports the tool-calling features structured output relies on'. Thrown as `OperationalError` (a transient/operational n8n-workflow error class).

Source

Thrown at packages/@n8n/ai-workflow-builder.ee/evaluations/evaluators/llm-judge/evaluators/base.ts:26

import type { EvaluationInput } from '../evaluation';

type EvaluatorChainInput = {
	userPrompt: string;
	generatedWorkflow: string;
	referenceSection: string;
	agentTextResponse?: string;
	workflowBefore?: string;
};

export function createEvaluatorChain<TResult extends Record<string, unknown>>(
	llm: BaseChatModel,
	schema: z.ZodType<TResult>,
	systemPrompt: string,
	humanTemplate: string,
): RunnableSequence<EvaluatorChainInput, TResult> {
	if (!llm.bindTools) {
		throw new OperationalError("LLM doesn't support binding tools");
	}

	const prompt = ChatPromptTemplate.fromMessages([
		new SystemMessage(systemPrompt),
		HumanMessagePromptTemplate.fromTemplate(humanTemplate),
	]);

	const llmWithStructuredOutput = llm.withStructuredOutput<TResult>(schema);

	return RunnableSequence.from<EvaluatorChainInput, TResult>([prompt, llmWithStructuredOutput]);
}

export async function invokeEvaluatorChain<TResult>(
	chain: Runnable<EvaluatorChainInput, TResult>,
	input: EvaluationInput,
	config?: RunnableConfig,
): Promise<TResult> {
	const referenceSection =

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Use a model class that implements `bindTools` — OpenAI `gpt-4o`/`gpt-4.1` family, Anthropic Claude, Google Gemini, or any LangChain `ChatModel` advertising tool calling.
  2. If you maintain a custom model adapter, implement `bindTools(tools)` (and `withStructuredOutput`) on the class.
  3. For tests, inject a fake model that defines `bindTools` and `withStructuredOutput` rather than a bare `BaseChatModel`.

Example fix

// before
const judge = new FakeChatModel({ content: '{}' }); // no bindTools
createEvaluatorChain(judge, schema, sys, human);
// after
class FakeJudge extends BaseChatModel {
  bindTools() { return this; }
  withStructuredOutput(s) { return { invoke: async () => ({...}) }; }
  _generate(): Promise<ChatResult> { return Promise.resolve({ generations: [{ message: new AIMessage('{}') }] }); }
  _llmType() { return 'fake-judge'; }
}
createEvaluatorChain(new FakeJudge({}), schema, sys, human);
Defensive patterns

Strategy: type-guard

Validate before calling

import type { BaseChatModel } from '@langchain/core/language_models/chat_models';

function supportsStructuredOutput(llm: BaseChatModel): boolean {
  return typeof (llm as { bindTools?: unknown }).bindTools === 'function';
}
if (!supportsStructuredOutput(judgeLlm)) {
  throw new Error('selected judge LLM does not support tool binding / structured output; pick a tool-capable model');
}
const chain = createEvaluatorChain(judgeLlm, schema, sys, human);

Type guard

function isToolCapableModel(llm: unknown): llm is BaseChatModel & { bindTools: Function; withStructuredOutput: Function } {
  return !!llm && typeof (llm as { bindTools?: unknown }).bindTools === 'function'
    && typeof (llm as { withStructuredOutput?: unknown }).withStructuredOutput === 'function';
}

Prevention

When it happens

Trigger: Passing a `BaseChatModel` subclass that does not implement `bindTools` — e.g. a dummy/fake model in tests, an older or non-tool-enabled LangChain integration, or a model wrapper that delegates to a backend without function calling. The check is `if (!llm.bindTools)`.

Common situations: Swapping in a smaller/cheaper local model (no tool support) to save cost during eval dev; using a stub model in a test harness; a LangChain version mismatch where `bindTools` was renamed/removed; misconfigured custom model adapter.

Related errors


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