n8n-io/n8n · error · Error

Failed to convert to Langchain content block: ${JSON.stringi

Error message

Failed to convert to Langchain content block: ${JSON.stringify(block)}

What it means

toLcContent() maps an n8n content block to a Langchain content block by checking block types in turn (text, reasoning, file, tool_call, invalid_tool_call, tool_result, citation, provider/non_standard). If none match, the block is of an unknown/unsupported type and cannot be represented in the Langchain content model, so it throws with the serialized block for diagnosis.

Source

Thrown at packages/@n8n/ai-utilities/src/converters/message.ts:317

	}
	if (isN8nCitationBlock(block)) {
		return {
			type: 'citation',
			source: block.source,
			url: block.url,
			title: block.title,
			startIndex: block.startIndex,
			endIndex: block.endIndex,
			citedText: block.text,
		} as unknown as LangchainMessages.ContentBlock;
	}
	if (isN8nProviderBlock(block)) {
		return {
			type: 'non_standard',
			value: block.value,
		} as LangchainMessages.ContentBlock.NonStandard;
	}
	throw new Error(`Failed to convert to Langchain content block: ${JSON.stringify(block)}`);
}

export function toLcMessage(message: Message): LangchainMessages.BaseMessage {
	const lcContent = message.content.map(toLcContent);

	switch (message.role) {
		case 'system':
			return new LangchainMessages.SystemMessage({
				content: lcContent,
				id: message.id,
				name: message.name,
			});
		case 'user':
			return new LangchainMessages.HumanMessage({
				content: lcContent,
				id: message.id,
				name: message.name,
			});

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Inspect the serialized block in the error to see its shape, then construct it using a supported type guard (text/reasoning/file/tool_*).
  2. If it's a genuinely new block type, add a branch + isN8nXBlock predicate to toLcContent in message.ts.
  3. Sanitize upstream: drop or coerce unknown blocks before conversion.

Example fix

// before
{ type: 'mystery', payload: {...} }

// after (coerce to text or drop)
{ type: 'text', text: JSON.stringify(rawBlock) }
Defensive patterns

Strategy: type-guard

Validate before calling

import { isN8nTextBlock, isN8nReasoningBlock, isN8nFileBlock } from '@n8n/ai-utilities';
function isKnownBlock(b: unknown): boolean {
  return isN8nTextBlock(b) || isN8nReasoningBlock(b) || isN8nFileBlock(b) /* or other supported guards */;
}
content.filter(isKnownBlock).map(toLcContent);

Type guard

function isSupportedBlock(b: unknown): boolean {
  return (
    typeof b === 'object' && b !== null && 'type' in b &&
    ['text','reasoning','file','tool_call','invalid_tool_call','tool_result','citation','non_standard'].includes((b as { type: string }).type)
  );
}

Prevention

When it happens

Trigger: Passing a content block missing a recognized discriminator (e.g. `{ type: 'something_new' }`); a malformed block where the type guard predicates all return false; a newly added n8n block type that this converter has not been taught to handle.

Common situations: A provider returned a block shape the converter doesn't know yet (version lag); a tool/handler emitted an ad-hoc block object; schema drift between n8n message types and the converter.

Related errors


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