FlowiseAI/Flowise · error · Error
ToolNode only accepts AIMessages as input.
Error message
ToolNode only accepts AIMessages as input.
What it means
ToolNode.run extracts the last message from input and checks `message._getType() !== 'ai'`. Tool nodes are only meaningful immediately after an AIMessage that carries `tool_calls` — they execute those calls and produce ToolMessages. Receiving a Human/System/Tool message means the graph is wired wrong.
Source
Thrown at packages/components/nodes/sequentialagents/Agent/Agent.ts:1007
// Check if input is an array of BaseMessage[]
if (Array.isArray(input)) {
messages = input
}
// Check if input is IStateWithMessages
else if ((input as IStateWithMessages).messages) {
messages = (input as IStateWithMessages).messages
}
// Handle MessagesState type
else {
messages = (input as MessagesState).messages
}
// Get the last message
const message = messages[messages.length - 1]
if (message._getType() !== 'ai') {
throw new Error('ToolNode only accepts AIMessages as input.')
}
// Extract all properties except messages for IStateWithMessages
const { messages: _, ...inputWithoutMessages } = Array.isArray(input) ? { messages: input } : input
const ChannelsWithoutMessages = {
chatId: this.options.chatId,
sessionId: this.options.sessionId,
input: this.inputQuery,
state: inputWithoutMessages
}
const outputs = await Promise.all(
(message as AIMessage).tool_calls?.map(async (call) => {
const tool = this.tools.find((tool) => tool.name === call.name)
if (tool === undefined) {
throw new Error(`Tool ${call.name} not found.`)
}
if (tool && (tool as any).setFlowObject) {View on GitHub (pinned to abe4a8601a)
Solutions
- Ensure the ToolNode is preceded by an Agent/LLM node whose output is an AIMessage with `tool_calls`.
- Check `state.messages[state.messages.length - 1]._getType()` before invoking the tool branch.
- Restructure the graph so the ToolNode always sits downstream of an LLM call.
Defensive patterns
Strategy: type-guard
Validate before calling
function lastMessageIsAI(input) {
const messages = Array.isArray(input) ? input : input?.messages ?? []
const last = messages[messages.length - 1]
return last?._getType?.() === 'ai'
}
if (!lastMessageIsAI(state)) {
throw new Error('ToolNode expects the last message to be an AIMessage with tool_calls.')
} Type guard
function isAIMessage(m): m is AIMessage {
return m != null && typeof m._getType === 'function' && m._getType() === 'ai'
} Prevention
- Always place ToolNode downstream of an Agent/LLM node that produces AIMessages with tool_calls.
- Never chain ToolNode directly after a HumanMessage or another ToolMessage.
- Inspect `state.messages.at(-1)._getType()` when debugging graph routing.
When it happens
Trigger: ToolNode receives a state (or BaseMessage[]) whose last message is not an AIMessage — e.g. after a HumanMessage, a SystemMessage, or another ToolMessage.
Common situations: Graph routes a HumanMessage straight into a ToolNode without an intervening LLM/agent step; ToolNode is chained after another ToolNode; conversation history's last entry is a tool result that wasn't followed by an AI turn; state was initialized with only a human message.
Related errors
- Tool not selected
- ${e}
- Google requires a tool name for each tool call response, and
- Unsupported message input
- Failed to load fs/promises. Make sure you are running in Nod
AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12).
Data as JSON: /api/errors/b47c321be4a2fe81.
Report an issue: GitHub.