subgraphs-manage-state: sample producing output different from that in docs #327

Closed
opened 2026-02-15 18:15:56 -05:00 by yindo · 1 comment
Owner

Originally created by @fauxbytes on GitHub (Aug 4, 2025).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph.js documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph.js rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).

Example Code

import { z } from "zod";
import { tool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, MessagesAnnotation, Annotation } from "@langchain/langgraph";

const getWeather = tool(async ({ city }) => {
  return `It's sunny in ${city}`;
}, {
  name: "get_weather",
  description: "Get the weather for a specific city",
  schema: z.object({
    city: z.string().describe("A city name")
  })
});

const rawModel = new ChatOpenAI({ model: "gpt-4o-mini" });
const model = rawModel.withStructuredOutput(getWeather);

// Extend the base MessagesAnnotation state with another field
const SubGraphAnnotation = Annotation.Root({
  ...MessagesAnnotation.spec,
  city: Annotation<string>,
});

const modelNode = async (state: typeof SubGraphAnnotation.State) => {
  const result = await model.invoke(state.messages);
  return { city: result.city };
};

const weatherNode = async (state: typeof SubGraphAnnotation.State) => {
  const result = await getWeather.invoke({ city: state.city });
  return {
    messages: [
      {
        role: "assistant",
        content: result,
      }
    ]
  };
};

const subgraph = new StateGraph(SubGraphAnnotation)
  .addNode("modelNode", modelNode)
  .addNode("weatherNode", weatherNode)
  .addEdge("__start__", "modelNode")
  .addEdge("modelNode", "weatherNode")
  .addEdge("weatherNode", "__end__")
  .compile({ interruptBefore: ["weatherNode"] });

import { MemorySaver } from "@langchain/langgraph";

const memory = new MemorySaver();

const RouterStateAnnotation = Annotation.Root({
  ...MessagesAnnotation.spec,
  route: Annotation<"weather" | "other">,
});

const routerModel = rawModel.withStructuredOutput(
  z.object({
    route: z.enum(["weather", "other"]).describe("A step that should execute next to based on the currnet input")
  }),
  {
    name: "router"
  }
);

const routerNode = async (state: typeof RouterStateAnnotation.State) => {
  const systemMessage = {
    role: "system",
    content: "Classify the incoming query as either about weather or not.",
  };
  const messages = [systemMessage, ...state.messages]
  const { route } = await routerModel.invoke(messages);
  return { route };
}

const normalLLMNode = async (state: typeof RouterStateAnnotation.State) => {
  const responseMessage = await rawModel.invoke(state.messages);
  return { messages: [responseMessage] };
};

const routeAfterPrediction = async (state: typeof RouterStateAnnotation.State) => {
  if (state.route === "weather") {
    return "weatherGraph";
  } else {
    return "normalLLMNode";
  }
};

const graph = new StateGraph(RouterStateAnnotation)
  .addNode("routerNode", routerNode)
  .addNode("normalLLMNode", normalLLMNode)
  .addNode("weatherGraph", subgraph)
  .addEdge("__start__", "routerNode")
  .addConditionalEdges("routerNode", routeAfterPrediction)
  .addEdge("normalLLMNode", "__end__")
  .addEdge("weatherGraph", "__end__")
  .compile({ checkpointer: memory });

const main = async () => {
  const config = { configurable: { thread_id: "1" } };

  const inputs = { messages: [{ role: "user", content: "hi!" }] };

  const stream = await graph.stream(inputs, { ...config, streamMode: "updates" });

  for await (const update of stream) {
    console.log(update);
  }
}

main().catch(console.error);

Error Message and Stack Trace (if applicable)

No response

Description

When executing the subgraphs sample, getting the following output:

{ routerNode: { route: 'other' } }
{
  normalLLMNode: {
    messages: [
      AIMessage {
        "id": "chatcmpl-C0iBEKNjx1Xedgffwqz6eIgMWg0cr",
        "content": "{\"route\":\"other\"}",
        "additional_kwargs": {},
        "response_metadata": {
          "tokenUsage": {
            "promptTokens": 52,
            "completionTokens": 5,
            "totalTokens": 57
          },
          "finish_reason": "stop",
          "model_name": "gpt-4o-mini-2024-07-18",
          "usage": {
            "prompt_tokens": 52,
            "completion_tokens": 5,
            "total_tokens": 57,
            "prompt_tokens_details": {
              "cached_tokens": 0,
              "audio_tokens": 0
            },
            "completion_tokens_details": {
              "reasoning_tokens": 0,
              "audio_tokens": 0,
              "accepted_prediction_tokens": 0,
              "rejected_prediction_tokens": 0
            }
          },
          "system_fingerprint": "fp_34a54ae93c"
        },
        "tool_calls": [],
        "invalid_tool_calls": [],
        "usage_metadata": {
          "output_tokens": 5,
          "input_tokens": 52,
          "total_tokens": 57,
          "input_token_details": {
            "audio": 0,
            "cache_read": 0
          },
          "output_token_details": {
            "audio": 0,
            "reasoning": 0
          }
        }
      }
    ]
  }
}

As-in, content is not the expected greeting response to the user's message.

If rawModel is inlined inside normalLLMNode, output is as-expected.

System Info

dependencies:
@langchain/core 0.3.66
@langchain/langgraph 0.3.12
@langchain/openai 0.6.3
dotenv 17.2.0
langchain 0.3.30
sqlite 5.1.1
typeorm 0.3.25
zod 4.0.5

devDependencies:
@types/node 24.0.14
ts-node 10.9.2
typescript 5.8.3

win11, node v20.18.3.

Originally created by @fauxbytes on GitHub (Aug 4, 2025). ### Checked other resources - [x] I added a very descriptive title to this issue. - [x] I searched the LangGraph.js documentation with the integrated search. - [x] I used the GitHub search to find a similar question and didn't find it. - [x] I am sure that this is a bug in LangGraph.js rather than my code. - [x] The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package). ### Example Code ```typescript import { z } from "zod"; import { tool } from "@langchain/core/tools"; import { ChatOpenAI } from "@langchain/openai"; import { StateGraph, MessagesAnnotation, Annotation } from "@langchain/langgraph"; const getWeather = tool(async ({ city }) => { return `It's sunny in ${city}`; }, { name: "get_weather", description: "Get the weather for a specific city", schema: z.object({ city: z.string().describe("A city name") }) }); const rawModel = new ChatOpenAI({ model: "gpt-4o-mini" }); const model = rawModel.withStructuredOutput(getWeather); // Extend the base MessagesAnnotation state with another field const SubGraphAnnotation = Annotation.Root({ ...MessagesAnnotation.spec, city: Annotation<string>, }); const modelNode = async (state: typeof SubGraphAnnotation.State) => { const result = await model.invoke(state.messages); return { city: result.city }; }; const weatherNode = async (state: typeof SubGraphAnnotation.State) => { const result = await getWeather.invoke({ city: state.city }); return { messages: [ { role: "assistant", content: result, } ] }; }; const subgraph = new StateGraph(SubGraphAnnotation) .addNode("modelNode", modelNode) .addNode("weatherNode", weatherNode) .addEdge("__start__", "modelNode") .addEdge("modelNode", "weatherNode") .addEdge("weatherNode", "__end__") .compile({ interruptBefore: ["weatherNode"] }); import { MemorySaver } from "@langchain/langgraph"; const memory = new MemorySaver(); const RouterStateAnnotation = Annotation.Root({ ...MessagesAnnotation.spec, route: Annotation<"weather" | "other">, }); const routerModel = rawModel.withStructuredOutput( z.object({ route: z.enum(["weather", "other"]).describe("A step that should execute next to based on the currnet input") }), { name: "router" } ); const routerNode = async (state: typeof RouterStateAnnotation.State) => { const systemMessage = { role: "system", content: "Classify the incoming query as either about weather or not.", }; const messages = [systemMessage, ...state.messages] const { route } = await routerModel.invoke(messages); return { route }; } const normalLLMNode = async (state: typeof RouterStateAnnotation.State) => { const responseMessage = await rawModel.invoke(state.messages); return { messages: [responseMessage] }; }; const routeAfterPrediction = async (state: typeof RouterStateAnnotation.State) => { if (state.route === "weather") { return "weatherGraph"; } else { return "normalLLMNode"; } }; const graph = new StateGraph(RouterStateAnnotation) .addNode("routerNode", routerNode) .addNode("normalLLMNode", normalLLMNode) .addNode("weatherGraph", subgraph) .addEdge("__start__", "routerNode") .addConditionalEdges("routerNode", routeAfterPrediction) .addEdge("normalLLMNode", "__end__") .addEdge("weatherGraph", "__end__") .compile({ checkpointer: memory }); const main = async () => { const config = { configurable: { thread_id: "1" } }; const inputs = { messages: [{ role: "user", content: "hi!" }] }; const stream = await graph.stream(inputs, { ...config, streamMode: "updates" }); for await (const update of stream) { console.log(update); } } main().catch(console.error); ``` ### Error Message and Stack Trace (if applicable) _No response_ ### Description When executing the [subgraphs sample](https://langchain-ai.lang.chat/langgraphjs/how-tos/subgraphs-manage-state), getting the following output: ``` { routerNode: { route: 'other' } } { normalLLMNode: { messages: [ AIMessage { "id": "chatcmpl-C0iBEKNjx1Xedgffwqz6eIgMWg0cr", "content": "{\"route\":\"other\"}", "additional_kwargs": {}, "response_metadata": { "tokenUsage": { "promptTokens": 52, "completionTokens": 5, "totalTokens": 57 }, "finish_reason": "stop", "model_name": "gpt-4o-mini-2024-07-18", "usage": { "prompt_tokens": 52, "completion_tokens": 5, "total_tokens": 57, "prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 }, "completion_tokens_details": { "reasoning_tokens": 0, "audio_tokens": 0, "accepted_prediction_tokens": 0, "rejected_prediction_tokens": 0 } }, "system_fingerprint": "fp_34a54ae93c" }, "tool_calls": [], "invalid_tool_calls": [], "usage_metadata": { "output_tokens": 5, "input_tokens": 52, "total_tokens": 57, "input_token_details": { "audio": 0, "cache_read": 0 }, "output_token_details": { "audio": 0, "reasoning": 0 } } } ] } } ``` As-in, `content` is not the expected greeting response to the user's message. If `rawModel` is inlined inside `normalLLMNode`, output is as-expected. ### System Info ``` dependencies: @langchain/core 0.3.66 @langchain/langgraph 0.3.12 @langchain/openai 0.6.3 dotenv 17.2.0 langchain 0.3.30 sqlite 5.1.1 typeorm 0.3.25 zod 4.0.5 devDependencies: @types/node 24.0.14 ts-node 10.9.2 typescript 5.8.3 ``` win11, node v20.18.3.
yindo closed this issue 2026-02-15 18:15:56 -05:00
Author
Owner

@dqbd commented on GitHub (Sep 9, 2025):

Hello! I think this might be due to non-deterministic behaviour of LLMs, trying the code snippet does not yield the same issue

We also now have an updated docs page: https://docs.langchain.com/oss/python/langgraph/use-subgraphs, so I think this issue is no longer relevant.

@dqbd commented on GitHub (Sep 9, 2025): Hello! I think this might be due to non-deterministic behaviour of LLMs, trying the code snippet does not yield the same issue We also now have an updated docs page: https://docs.langchain.com/oss/python/langgraph/use-subgraphs, so I think this issue is no longer relevant.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#327