[GH-ISSUE #1239] [langgraph] Command goto does not override existing edges, creates parallel execution (docs unclear) #178

Open
opened 2026-02-17 17:19:20 -05:00 by yindo · 0 comments
Owner

Originally created by @luizzappa on GitHub (Nov 1, 2025).
Original GitHub issue: https://github.com/langchain-ai/docs/issues/1239

Description

In my use case, I sometimes need to jump to a specific node using a Command with a goto.
I expected that calling goto would ignore the existing edges from the current node and continue execution only from the specified node onward.

Expected behavior:

__START__ -> A -> B -> C -> D -> __END__

// If node A returns Command({ goto: 'C' }), I expected:
__START__ -> A ---goto---> C -> D -> __END__   // B is skipped

However, what actually happens is that the goto does not cancel the existing transition.
Instead, it enqueues the target node in parallel while the normal edge (A -> B) still executes.
The result is a duplicated flow starting from the goto destination:

__START__ -> A  ->   B        -> C -> D -> __END__
                    \
                     ---goto---> C -> D -> __END__

It took quite a while to debug this because it looked like my nodes were being called multiple times before I realized this is how goto works with edges..

If this behavior is intentional, it would be very helpful to clarify this in the documentation. I initially assumed goto was a “jump” command rather than a “branch”.

DX Improvement Ideas

Provide a flag or configuration option to indicate that goto should cancel the current node’s outgoing edges before proceeding.

Minimal Reproducible Example

import { Annotation, Command } from "@langchain/langgraph";

const StateAnnotation = Annotation.Root({
  foo: Annotation<string>({
    reducer: (o, n) => o + "|" + n,
  }),
});

const nodeA = async (state: typeof StateAnnotation.State) => {
  console.log("Called A", state);
  console.log("calling command to go nodeC");
  return new Command({
    update: { foo: "a" },
    goto: "nodeC",
  });
};

const nodeB = async (state: typeof StateAnnotation.State) => {
  console.log("Called B", state);
  return { foo: "b" };
};

const nodeC = async (state: typeof StateAnnotation.State) => {
  console.log("Called C", state);
  return { foo: "c" };
};

const nodeD = async (state: typeof StateAnnotation.State) => {
  console.log("Called D", state);
  return { foo: "d" };
};

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

export const graph = new StateGraph(StateAnnotation)
  .addNode("nodeA", nodeA, { ends: ["nodeC", "nodeA"] })
  .addNode("nodeB", nodeB)
  .addNode("nodeC", nodeC)
  .addNode("nodeD", nodeD)
  .addEdge("__start__", "nodeA")
  .addEdge("nodeA", "nodeB")
  .addEdge("nodeB", "nodeC")
  .addEdge("nodeC", "nodeD")
  .addEdge("nodeD", "__end__")
  .compile();

Output

Called A { foo: 'i' }
calling command to go nodeC
Called B { foo: 'i|a' }
Called C { foo: 'i|a' }
Called C { foo: 'i|a|b|c' }
Called D { foo: 'i|a|b|c' }
Called D { foo: 'i|a|b|c|c|d' }

Note that both C and D are executed twice.

Originally created by @luizzappa on GitHub (Nov 1, 2025). Original GitHub issue: https://github.com/langchain-ai/docs/issues/1239 # Description In my use case, I sometimes need to _jump_ to a specific node using a `Command` with a `goto`. I expected that calling `goto` would **ignore the existing edges** from the current node and continue execution **only from the specified node onward**. Expected behavior: ``` __START__ -> A -> B -> C -> D -> __END__ // If node A returns Command({ goto: 'C' }), I expected: __START__ -> A ---goto---> C -> D -> __END__ // B is skipped ``` However, what actually happens is that the `goto` **does not cancel** the existing transition. Instead, it enqueues the target node in **parallel** while the normal edge (`A -> B`) still executes. The result is a _duplicated flow_ starting from the goto destination: ``` __START__ -> A -> B -> C -> D -> __END__ \ ---goto---> C -> D -> __END__ ``` It took quite a while to debug this because it looked like my nodes were being called multiple times before I realized this is how `goto` works with edges.. If this behavior is intentional, it would be very helpful to clarify this in the documentation. I initially assumed `goto` was a “jump” command rather than a “branch”. # DX Improvement Ideas Provide a flag or configuration option to indicate that `goto` should **cancel** the current node’s outgoing edges before proceeding. # Minimal Reproducible Example ```typescript import { Annotation, Command } from "@langchain/langgraph"; const StateAnnotation = Annotation.Root({ foo: Annotation<string>({ reducer: (o, n) => o + "|" + n, }), }); const nodeA = async (state: typeof StateAnnotation.State) => { console.log("Called A", state); console.log("calling command to go nodeC"); return new Command({ update: { foo: "a" }, goto: "nodeC", }); }; const nodeB = async (state: typeof StateAnnotation.State) => { console.log("Called B", state); return { foo: "b" }; }; const nodeC = async (state: typeof StateAnnotation.State) => { console.log("Called C", state); return { foo: "c" }; }; const nodeD = async (state: typeof StateAnnotation.State) => { console.log("Called D", state); return { foo: "d" }; }; import { StateGraph } from "@langchain/langgraph"; export const graph = new StateGraph(StateAnnotation) .addNode("nodeA", nodeA, { ends: ["nodeC", "nodeA"] }) .addNode("nodeB", nodeB) .addNode("nodeC", nodeC) .addNode("nodeD", nodeD) .addEdge("__start__", "nodeA") .addEdge("nodeA", "nodeB") .addEdge("nodeB", "nodeC") .addEdge("nodeC", "nodeD") .addEdge("nodeD", "__end__") .compile(); ``` **Output** ``` Called A { foo: 'i' } calling command to go nodeC Called B { foo: 'i|a' } Called C { foo: 'i|a' } Called C { foo: 'i|a|b|c' } Called D { foo: 'i|a|b|c' } Called D { foo: 'i|a|b|c|c|d' } ``` Note that both `C` and `D` are executed twice.
yindo added the external label 2026-02-17 17:19:20 -05:00
yindo changed title from [langgraph] Command goto does not override existing edges, creates parallel execution (docs unclear) to [GH-ISSUE #1239] [langgraph] Command goto does not override existing edges, creates parallel execution (docs unclear) 2026-06-05 17:25:29 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/docs#178