{"record":{"id":"12e6d67c49e29c32","repo":"langchain-ai/langchain","slug":"runnable-step-has-no-last-node","errorCode":null,"errorMessage":"Runnable {step} has no last node","messagePattern":"Runnable (.+?) has no last node","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/runnables/base.py","lineNumber":4112,"sourceCode":"        from langchain_core.runnables.graph import Graph  # noqa: PLC0415\n\n        graph = Graph()\n        input_node = graph.add_node(self.get_input_schema(config))\n        output_node = graph.add_node(self.get_output_schema(config))\n        for step in self.steps__.values():\n            step_graph = step.get_graph()\n            step_graph.trim_first_node()\n            step_graph.trim_last_node()\n            if not step_graph:\n                graph.add_edge(input_node, output_node)\n            else:\n                step_first_node, step_last_node = graph.extend(step_graph)\n                if not step_first_node:\n                    msg = f\"Runnable {step} has no first node\"\n                    raise ValueError(msg)\n                if not step_last_node:\n                    msg = f\"Runnable {step} has no last node\"\n                    raise ValueError(msg)\n                graph.add_edge(input_node, step_first_node)\n                graph.add_edge(step_last_node, output_node)\n\n        return graph\n\n    @override\n    def __repr__(self) -> str:\n        map_for_repr = \",\\n  \".join(\n            f\"{k}: {indent_lines_after_first(repr(v), '  ' + k + ': ')}\"\n            for k, v in self.steps__.items()\n        )\n        return \"{\\n  \" + map_for_repr + \"\\n}\"\n\n    @override\n    def invoke(\n        self, input: Input, config: RunnableConfig | None = None, **kwargs: Any\n    ) -> dict[str, Any]:\n        # setup callbacks","sourceCodeStart":4094,"sourceCodeEnd":4130,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/runnables/base.py#L4094-L4130","documentation":"Mirror of the missing-first-node check in `RunnableParallel.get_graph`: after a branch's sub-graph is extended, the code verifies it also produced a last node so an edge can be drawn to the shared output schema node. If the trimmed sub-graph has a first node but no last node, a `ValueError` is raised. This indicates a branch runnable whose graph is internally inconsistent.","triggerScenarios":"A `RunnableParallel` branch whose custom `get_graph()` returns a graph with nodes but no designated last node (e.g. built by adding nodes/edges manually without letting `Graph` track the last node), then calling `.get_graph()` on the parallel runnable.","commonSituations":"Hand-built `Graph` objects in a custom runnable that call `add_node`/`add_edge` in an order where the graph cannot infer an end node; graph construction bugs in custom runnables; visualization of complex parallel pipelines.","solutions":["Fix the custom `get_graph()` so the graph ends in a definite node (the last `add_node`'s node, or set edges so a terminal node exists).","Simplest robust override: single node `graph.add_node('name', self)`.","Replace the branch with `RunnableLambda` to inherit a correct graph.","Test custom graphs with `assert graph.last_node() is not None` before returning."],"exampleFix":"// before\ndef get_graph(self, config=None):\n    g = Graph()\n    n = g.add_node('start', self)\n    g.add_edge(n, g.add_node('end', self))  # malformed ordering\n    return g\n\n// after\ndef get_graph(self, config=None):\n    g = Graph()\n    g.add_node('my_runnable', self)\n    return g","handlingStrategy":"validation","validationCode":"from langchain_core.runnables import Runnable\n\ndef has_terminal_node(step: Runnable) -> bool:\n    g = step.get_graph()\n    g.trim_first_node()\n    return g.last_node() is not None or not g.nodes","typeGuard":null,"tryCatchPattern":"try:\n    graph = parallel.get_graph()\nexcept ValueError as e:\n    if 'has no last node' in str(e):\n        # fix the branch graph to end in a definite node\n        raise\n    raise","preventionTips":["Keep custom get_graph() minimal: one node via add_node.","Ensure hand-built Graphs have a node with no outgoing edges.","Validate graph.last_node() before returning from get_graph()."],"tags":["runnable","runnable-parallel","graph","get-graph"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}