n8n-io/n8n · error · OperationalError
Invalid message type
Error message
Invalid message type
What it means
Thrown by getMessagesPromptTemplates in promptUtils when a message template's 'type' does not match any of SystemMessagePromptTemplate, AIMessagePromptTemplate, or HumanMessagePromptTemplate (matched via lc_name()). The message type is unrecognized, so no LangChain prompt-template class can be selected.
Source
Thrown at packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/methods/promptUtils.ts:53
context,
itemIndex,
messages,
}: {
context: IExecuteFunctions;
itemIndex: number;
messages: MessageTemplate[];
}): Promise<BaseMessagePromptTemplateLike[]> {
return await Promise.all(
messages.map(async (message) => {
// Find the appropriate message class based on type
const messageClass = [
SystemMessagePromptTemplate,
AIMessagePromptTemplate,
HumanMessagePromptTemplate,
].find((m) => m.lc_name() === message.type);
if (!messageClass) {
throw new OperationalError('Invalid message type', {
extra: { messageType: message.type },
});
}
// Handle image messages specially for human messages
if (messageClass === HumanMessagePromptTemplate && message.messageType !== 'text') {
return await createImageMessage({ context, itemIndex, message });
}
// Process text messages
// Escape curly braces in the message to prevent LangChain from treating them as variables
return messageClass.fromTemplate(
(message.message || '').replace(/[{}]/g, (match) => match + match),
);
}),
);
}
View on GitHub (pinned to 5ac6606e81)
Solutions
- Open the node's Messages config and set each message's type to one of: system, ai, or human.
- If hand-editing workflow JSON, ensure each message.type is exactly one of the supported lc_name() values.
- Delete and re-create the offending message row to clear stale fixedCollection state.
- Upgrade/migrate the workflow through the supported node version path rather than editing raw JSON.
Example fix
// before
const messageClass = [SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate]
.find((m) => m.lc_name() === message.type);
if (!messageClass) {
throw new OperationalError('Invalid message type', { extra: { messageType: message.type } });
}
// after — enumerate accepted values in the error
const ACCEPTED = ['system', 'ai', 'human'];
if (!ACCEPTED.includes(message.type)) {
throw new OperationalError(`Invalid message type '${message.type}'. Accepted: ${ACCEPTED.join(', ')}`, {
extra: { messageType: message.type },
});
} Defensive patterns
Strategy: validation
Validate before calling
const VALID_TYPES = new Set(['system', 'ai', 'human']);
for (const m of messages) {
if (!VALID_TYPES.has(m.type)) {
throw new Error(`Invalid message type '${m.type}'. Must be system, ai, or human.`);
}
} Type guard
function isValidMessageType(t: unknown): t is 'system' | 'ai' | 'human' {
return t === 'system' || t === 'ai' || t === 'human';
} Prevention
- Restrict message types to system/ai/human when authoring templates.
- Recreate stale message rows through the UI rather than editing JSON.
- Migrate workflows through supported version paths.
When it happens
Trigger: A message row in the Basic LLM Chain 'Messages' fixedCollection has a 'type' value that is not 'system', 'ai', or 'human' (e.g. a typo, a legacy value, or an injected custom value). The .find() returns undefined and OperationalError is thrown with the bad type recorded in extra.messageType.
Common situations: Workflow exported from an older node version with a renamed message type; user hand-edited the workflow JSON and introduced an invalid type; a template was migrated partially.
Related errors
- Invalid message type. Only imageBinary and imageUrl are supp
- The ‘text‘ parameter is empty.
- The ‘text‘ parameter is empty.
- The ‘prompt’ parameter is empty.
- No binary data found, please connect a binary to the input i
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/669f5bdae55fc12c.
Report an issue: GitHub.