langchain-ai/langchain · error · ValueError

Runnable {dep} has no first node

Error message

Runnable {dep} has no first node

What it means

`RunnableEachBase.get_graph()` builds a graph by trimming each dependency runnable's sub-graph and extending it between input/output schema nodes. If a dependency's trimmed graph extends without producing a first node, a `ValueError` is raised — the fan-in/fan-out structure of `RunnableEach` cannot be drawn. Root cause is a dependency with an empty or malformed `get_graph()`.

Source

Thrown at libs/core/langchain_core/runnables/base.py:5106

    def get_graph(self, config: RunnableConfig | None = None) -> Graph:
        if deps := self.deps:
            # Import locally to prevent circular import
            from langchain_core.runnables.graph import Graph  # noqa: PLC0415

            graph = Graph()
            input_node = graph.add_node(self.get_input_schema(config))
            output_node = graph.add_node(self.get_output_schema(config))
            for dep in deps:
                dep_graph = dep.get_graph()
                dep_graph.trim_first_node()
                dep_graph.trim_last_node()
                if not dep_graph:
                    graph.add_edge(input_node, output_node)
                else:
                    dep_first_node, dep_last_node = graph.extend(dep_graph)
                    if not dep_first_node:
                        msg = f"Runnable {dep} has no first node"
                        raise ValueError(msg)
                    if not dep_last_node:
                        msg = f"Runnable {dep} has no last node"
                        raise ValueError(msg)
                    graph.add_edge(input_node, dep_first_node)
                    graph.add_edge(dep_last_node, output_node)
        else:
            graph = super().get_graph(config)

        return graph

    @override
    def __eq__(self, other: object) -> bool:
        if isinstance(other, RunnableLambda):
            if hasattr(self, "func") and hasattr(other, "func"):
                return self.func == other.func
            if hasattr(self, "afunc") and hasattr(other, "afunc"):
                return self.afunc == other.afunc
            return False

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Fix the dependency's `get_graph()` to return a graph containing at least one node.
  2. Wrap the mapped runnable's inner function in `RunnableLambda` before `.map()`.
  3. Check each dependency: `assert dep.get_graph().first_node() is not None`.
  4. Skip graph rendering for exotic runnables, or register a simple proxy node for them.

Example fix

// before
batch = my_custom_runnable.map()  # custom get_graph is empty
batch.get_graph().draw_ascii()  # ValueError

// after
batch = RunnableLambda(my_fn).map()
batch.get_graph().draw_ascii()  # ok
Defensive patterns

Strategy: validation

Validate before calling

def deps_stitchable(mapped) -> bool:
    for dep in getattr(mapped, 'deps', []):
        g = dep.get_graph()
        g.trim_first_node()
        g.trim_last_node()
        if g.nodes:
            return True
    return len(getattr(mapped, 'deps', [])) == 0

Try / catch

try:
    g = mapped.get_graph()
except ValueError as e:
    if 'has no first node' in str(e):
        # rebuild the mapped runnable around a RunnableLambda
        raise
    raise

Prevention

When it happens

Trigger: `my_runnable.map()` (which produces a `RunnableEach`) where the bound runnable or a dependency has a broken/empty `get_graph()`, then calling `.get_graph()` or graph visualization on the mapped runnable.

Common situations: Custom `Runnable` subclasses used with `.map()` without a proper `get_graph` override; visualizing batch-processing pipelines; third-party runnables with graph-API incompatibilities.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/80975427f79d21c4. Report an issue: GitHub.