Perceived stage duplication #13

Closed
opened 2026-02-15 17:05:18 -05:00 by yindo · 2 comments
Owner

Originally created by @hutchic on GitHub (Mar 7, 2024).

I'm experiencing an unexpected behavior with LangGraph where the final state of a workflow (allegedly) contains duplicated actions, despite individual node functions not duplicating these actions themselves. This duplication seems to occur between node transitions and is observable even in a minimal workflow setup.

Steps to Reproduce

  1. Create a minimal workflow with two nodes where the first node adds a single action to the state, and the second node simply logs the state and returns it without modifications.
  2. Run the workflow and observe the final state logged after the workflow completion.

Expected behavior: The final state should reflect the actions as modified and passed through by each node without duplication.

Actual behavior: The final state contains duplicated actions, even though each node in the workflow only interacts with the state as intended (adding or logging actions).

Minimal Workflow Example

    const workflow = new StateGraph({
        channels: {
            actions: {
                value: (x: any, y: any) => [...x, ...y],
                default: () => [],
            }
            // You can add more channels as needed
        },
    });

    // Define the first node, which just adds an action
    const firstNode = async (state: { actions: any; }) => {
        console.log('[firstNode] Start:', JSON.stringify(state));
        const newState = {
            ...state,
            actions: [...state.actions, { content: 'Initial action', type: 'manualAction' }],
        };
        console.log('[firstNode] End:', JSON.stringify(newState));
        return newState;
    };

    // Define the second node, which ideally does nothing but logs the state
    const secondNode = async (state: any) => {
        console.log('[secondNode] Start:', JSON.stringify(state));
        // Here, we just log and return the state without modification
        console.log('[secondNode] End:', JSON.stringify(state));
        return state;
    };

    // Add the nodes to the workflow
    workflow.addNode('firstNode', new RunnableLambda({ func: firstNode }));
    workflow.addNode('secondNode', new RunnableLambda({ func: secondNode }));

    // Define the workflow structure
    workflow.setEntryPoint('firstNode');
    workflow.addEdge('firstNode', 'secondNode');
    workflow.setFinishPoint('secondNode');

    // Execute the workflow
    console.log('Compiling and running the minimal workflow');
    const result = await workflow.compile().invoke({});
    console.log('Minimal workflow completed. Final state:', JSON.stringify(result));
    return result;

Console output

    [firstNode] Start: {"actions":[]}
[firstNode] End: {"actions":[{"content":"Initial action","type":"manualAction"}]}
[secondNode] Start: {"actions":[{"content":"Initial action","type":"manualAction"}]}
[secondNode] End: {"actions":[{"content":"Initial action","type":"manualAction"}]}
Minimal workflow completed. Final state: {"actions":[{"content":"Initial action","type":"manualAction"},{"content":"Initial action","type":"manualAction"}]}

Browser output

{
  "actions": [
    {
      "content": "Initial action",
      "type": "manualAction"
    },
    {
      "content": "Initial action",
      "type": "manualAction"
    }
  ]
}
Originally created by @hutchic on GitHub (Mar 7, 2024). I'm experiencing an unexpected behavior with LangGraph where the final state of a workflow (allegedly) contains duplicated actions, despite individual node functions not duplicating these actions themselves. This duplication seems to occur between node transitions and is observable even in a minimal workflow setup. ## Steps to Reproduce 1. Create a minimal workflow with two nodes where the first node adds a single action to the state, and the second node simply logs the state and returns it without modifications. 2. Run the workflow and observe the final state logged after the workflow completion. **Expected behavior**: The final state should reflect the actions as modified and passed through by each node without duplication. **Actual behavior**: The final state contains duplicated actions, even though each node in the workflow only interacts with the state as intended (adding or logging actions). ## Minimal Workflow Example ```javascript const workflow = new StateGraph({ channels: { actions: { value: (x: any, y: any) => [...x, ...y], default: () => [], } // You can add more channels as needed }, }); // Define the first node, which just adds an action const firstNode = async (state: { actions: any; }) => { console.log('[firstNode] Start:', JSON.stringify(state)); const newState = { ...state, actions: [...state.actions, { content: 'Initial action', type: 'manualAction' }], }; console.log('[firstNode] End:', JSON.stringify(newState)); return newState; }; // Define the second node, which ideally does nothing but logs the state const secondNode = async (state: any) => { console.log('[secondNode] Start:', JSON.stringify(state)); // Here, we just log and return the state without modification console.log('[secondNode] End:', JSON.stringify(state)); return state; }; // Add the nodes to the workflow workflow.addNode('firstNode', new RunnableLambda({ func: firstNode })); workflow.addNode('secondNode', new RunnableLambda({ func: secondNode })); // Define the workflow structure workflow.setEntryPoint('firstNode'); workflow.addEdge('firstNode', 'secondNode'); workflow.setFinishPoint('secondNode'); // Execute the workflow console.log('Compiling and running the minimal workflow'); const result = await workflow.compile().invoke({}); console.log('Minimal workflow completed. Final state:', JSON.stringify(result)); return result; ``` Console output ``` [firstNode] Start: {"actions":[]} [firstNode] End: {"actions":[{"content":"Initial action","type":"manualAction"}]} [secondNode] Start: {"actions":[{"content":"Initial action","type":"manualAction"}]} [secondNode] End: {"actions":[{"content":"Initial action","type":"manualAction"}]} Minimal workflow completed. Final state: {"actions":[{"content":"Initial action","type":"manualAction"},{"content":"Initial action","type":"manualAction"}]} ``` Browser output ``` { "actions": [ { "content": "Initial action", "type": "manualAction" }, { "content": "Initial action", "type": "manualAction" } ] } ```
yindo closed this issue 2026-02-15 17:05:18 -05:00
Author
Owner

@NERLOE commented on GitHub (Mar 7, 2024):

Hey @hutchic.

In the actions value function, you concatenate the x and y, which is the previous state and newly added values you get from the agent node.

actions: {
    value: (x: any, y: any) => [...x, ...y],
    default: () => [],
}

This means that every action you return in the AgentNode is being added to the current actions array.

So when you return this in the first node:

const newState = {
    ...state,
    actions: [...state.actions, { content: 'Initial action', type: 'manualAction' }],
};

return newState;

and this in the second node:

return state;

You actually append the state in the two examples, so that's why you get duplicates.

If you don't want to add anything in the second node, you should just return an empty actions object, like so:

{
    ...state,
    actions: [],
};
@NERLOE commented on GitHub (Mar 7, 2024): Hey @hutchic. In the actions `value` function, you concatenate the x and y, which is the previous state and newly added values you get from the agent node. ```ts actions: { value: (x: any, y: any) => [...x, ...y], default: () => [], } ``` This means that every action you return in the AgentNode is being added to the current actions array. So when you return this in the first node: ```ts const newState = { ...state, actions: [...state.actions, { content: 'Initial action', type: 'manualAction' }], }; return newState; ``` and this in the second node: ```ts return state; ``` You actually append the state in the two examples, so that's why you get duplicates. If you don't want to add anything in the second node, you should just return an empty actions object, like so: ```ts { ...state, actions: [], }; ```
Author
Owner

@hutchic commented on GitHub (Mar 7, 2024):

Yup that did the trick. Thank you!

@hutchic commented on GitHub (Mar 7, 2024): Yup that did the trick. Thank you!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#13