langchain-ai/langgraph · error · ValueError
No message found in input
Error message
No message found in input
What it means
Generic input-shape guard in ToolNode's input coercion (_parse_input): the incoming value is neither a list of messages, a ToolCallWithContext payload, a dict containing the messages key, nor an object exposing that attribute. ToolNode cannot locate any messages, so it refuses to process the input.
Solutions
- Invoke ToolNode with a list of messages, a dict like {"messages": [...]}, or the standard graph state
- If using a custom messages_key, ensure the input carries that exact key
- Route from the model node via tools_condition so input shape matches expectations
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at libs/prebuilt/langgraph/prebuilt/tool_node.py:1255 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of langchain-ai/langgraph@38031739e5 (2026-08-26).
Data as JSON: /api/errors/2ff986764a28b333.
Report an issue: GitHub.
Appendix: source
Thrown at libs/prebuilt/langgraph/prebuilt/tool_node.py:1255
isinstance(input, dict) and input.get("__type") == "tool_call_with_context"
):
# Handle ToolCallWithContext from Send API
# Type checkers cannot narrow correctly since the signature
# for input contains dict[str, Any]. We'd need to narrow dict[str, Any]
# before we can apply correct typing.
input_with_ctx = cast("ToolCallWithContext", input)
input_type = "tool_calls"
return [input_with_ctx["tool_call"]], input_type
elif isinstance(input, dict) and (
messages := input.get(self._messages_key, [])
):
input_type = "dict"
elif messages := getattr(input, self._messages_key, []):
# Assume dataclass-like state that can coerce from dict
input_type = "dict"
else:
msg = "No message found in input"
raise ValueError(msg)
try:
latest_ai_message = next(
m for m in reversed(messages) if isinstance(m, AIMessage)
)
except StopIteration:
msg = "No AIMessage found in input"
raise ValueError(msg)
tool_calls = list(latest_ai_message.tool_calls)
return tool_calls, input_type
def _validate_tool_call(self, call: ToolCall) -> ToolMessage | None:
requested_tool = call["name"]
if requested_tool not in self.tools_by_name:
all_tool_names = list(self.tools_by_name.keys())
content = INVALID_TOOL_NAME_ERROR_TEMPLATE.format(
requested_tool=requested_tool,View on GitHub (pinned to 38031739e5)