langchain-ai/langchain · error · ValueError
Runnable {step} has no first node
Error message
Runnable {step} has no first node What it means
While building the compute graph for a `RunnableSequence`, each step's own graph has its boundary nodes trimmed (first node trimmed for non-first steps, last node trimmed for non-last steps) and is then spliced in with `graph.extend`. If a step's trimmed graph yields no first node, the sequence cannot be stitched together and a `ValueError` is raised. This almost always means a custom `Runnable` subclass returns an empty or malformed graph from `get_graph()`.
Source
Thrown at libs/core/langchain_core/runnables/base.py:3307
Raises:
ValueError: If a `Runnable` has no first or last node.
"""
# Import locally to prevent circular import
from langchain_core.runnables.graph import Graph # noqa: PLC0415
graph = Graph()
for step in self.steps:
current_last_node = graph.last_node()
step_graph = step.get_graph(config)
if step is not self.first:
step_graph.trim_first_node()
if step is not self.last:
step_graph.trim_last_node()
step_first_node, _ = graph.extend(step_graph)
if not step_first_node:
msg = f"Runnable {step} has no first node"
raise ValueError(msg)
if current_last_node:
graph.add_edge(current_last_node, step_first_node)
return graph
@override
def __repr__(self) -> str:
return "\n| ".join(
repr(s) if i == 0 else indent_lines_after_first(repr(s), "| ")
for i, s in enumerate(self.steps)
)
@overload
def __or__(
self, other: Mapping[str, Any]
) -> RunnableSerializable[Input, dict[str, Any]]: ...
@overloadView on GitHub (pinned to e32fa9a52e)
Solutions
- Override `get_graph()` in your custom `Runnable` to return a graph with at least a real node: `graph = Graph(); graph.add_node('my_runnable', self); return graph`.
- Check `len(my_runnable.get_graph().nodes) > 0` before adding it to a sequence.
- Wrap the offending runnable in a `RunnableLambda` (`RunnableLambda(fn)`), whose graph implementation is well-formed.
- If the runnable is trivially single-node, ensure `trim_first_node()`/`trim_last_node()` leave at least the boundary node the sequence needs.
Example fix
// before
class MyRunnable(Runnable):
def invoke(self, x, config=None):
return x
# get_graph not overridden -> may extend to no first node
// after
class MyRunnable(Runnable):
def invoke(self, x, config=None):
return x
def get_graph(self, config=None):
graph = Graph()
graph.add_node('MyRunnable', self)
return graph Defensive patterns
Strategy: validation
Validate before calling
from langchain_core.runnables import Runnable
def graph_is_stitchable(step: Runnable) -> bool:
g = step.get_graph()
g.trim_first_node()
g.trim_last_node()
return bool(g.nodes) Try / catch
try:
graph = sequence.get_graph()
except ValueError as e:
if 'has no first node' in str(e):
# identify and fix/replace the offending step
raise
raise Prevention
- Override get_graph() in custom Runnables to add at least one node.
- Wrap unknown runnables in RunnableLambda before composing sequences.
- Test get_graph() on every custom runnable as part of its unit tests.
When it happens
Trigger: Embedding a custom `Runnable` whose `get_graph()` returns a `Graph` with no nodes (or only a last node) inside a `RunnableSequence`, then calling `sequence.get_graph()`, `.get_repr()`, or rendering with `LangChain`/`langgraph` visualization tools that call `get_graph().draw_ascii()`/`.print_ascii()`.
Common situations: Writing a lightweight custom `Runnable` and not overriding (or incorrectly overriding) `get_graph()`; a custom runnable whose graph gets fully consumed by `trim_first_node()` because it has a single node; visualizing pipelines containing third-party runnables with broken graph support.
Related errors
- Runnable {step} has no last node
- Runnable {dep} has no first node
- Runnable {dep} has no last node
- RunnableSequence must have at least {_RUNNABLE_SEQUENCE_MIN_
- Expected a generator function type for `transform`.Instead g
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/b779e34c8ef66431.
Report an issue: GitHub.