[GH-ISSUE #52] [Bug] InvalidUpdateError: LastValue channel cannot receive multiple values in same step when coordinator agent calls write_todos multiple times #13

Closed
opened 2026-02-16 06:16:48 -05:00 by yindo · 1 comment
Owner

Originally created by @supperdsj on GitHub (Nov 9, 2025).
Original GitHub issue: https://github.com/langchain-ai/deepagentsjs/issues/52

[Bug] InvalidUpdateError: LastValue channel cannot receive multiple values in same step when coordinator agent calls write_todos multiple times

🐛 Bug Description

When a coordinator agent attempts to call write_todos multiple times within the same execution step (especially in parallel task scenarios), LangGraph throws an InvalidUpdateError due to the LastValue channel limitation.

Error Message

InvalidUpdateError: Invalid update for channel "todos" with values [[{"content":"...","status":"in_progress"},...],[{"content":"...","status":"in_progress"},...]]: LastValue can only receive one value per step.

Troubleshooting URL: https://docs.langchain.com/oss/javascript/langgraph/INVALID_CONCURRENT_GRAPH_UPDATE/

🔍 Environment

  • Package: @langchain/langgraph (JavaScript/TypeScript)
  • Version: Latest (as of Nov 2025)
  • Framework: DeepAgents workflow with coordinator + sub-agents pattern
  • Runtime: Node.js

📋 Steps to Reproduce

Scenario

A coordinator agent orchestrates multiple parallel sub-tasks (e.g., querying weather for multiple cities simultaneously).

Workflow Structure

// Main coordinator agent
const mainPrompt = `
You are a workflow coordinator.

Task: Query weather for Beijing, Shanghai, and Shenzhen in parallel, then generate a comparison report.

Execution Plan:
1. Parallel execution:
   - task('city_weather_searcher', 'Query Beijing weather')
   - task('city_weather_searcher', 'Query Shanghai weather')
   - task('city_weather_searcher', 'Query Shenzhen weather')
2. task('weather_comparator', 'Generate comparison report')
`;

// Sub-agents
const subAgents = [
  { id: 'city_weather_searcher', tools: ['web_search'] },
  { id: 'weather_comparator', tools: [] }
];

Execution Flow

Coordinator Agent Model Start
↓
Model End - Returns 8 tool calls:
  1. write_todos (1st call) ← Creates todos
  2. task('city_weather_searcher', 'Beijing')
  3. task('city_weather_searcher', 'Shanghai')
  4. task('city_weather_searcher', 'Shenzhen')
  5. write_todos (2nd call) ← Duplicate! 🔥
  6. task('city_weather_searcher', 'Beijing')
  7. task('city_weather_searcher', 'Shanghai')
  8. task('city_weather_searcher', 'Shenzhen')
↓
Execute tool calls
↓
❌ InvalidUpdateError: LastValue channel conflict

Minimal Reproduction

import { createDeepAgent } from '@langchain/langgraph';

const agent = createDeepAgent({
  mainPrompt: `
    Coordinate parallel tasks:
    1. Call task('worker', 'Task A')
    2. Call task('worker', 'Task B')
    3. Call task('worker', 'Task C')
  `,
  subAgents: [
    { id: 'worker', systemPrompt: 'Execute the task', tools: [] }
  ]
});

// This may trigger the error if LLM calls write_todos twice
await agent.invoke({ messages: [{ role: 'user', content: 'Start' }] });

🎯 Root Cause

  1. LLM Behavior: The coordinator agent's LLM generates duplicate write_todos calls in the same inference step, possibly because:

    • It interprets the parallel tasks as requiring separate todo lists
    • The prompt doesn't explicitly prohibit duplicate calls
    • The model's reasoning leads to redundant tool invocations
  2. LangGraph Limitation: The todos channel uses LastValue reducer, which cannot accept multiple values in a single step.

  3. Trigger Condition: This issue is more likely to occur when:

    • Multiple parallel tasks are involved (3+ parallel tasks)
    • The coordinator needs to orchestrate complex workflows
    • The prompt is ambiguous about todo management

💡 Expected Behavior

One of the following should happen:

Option A (Preferred): LangGraph should handle multiple updates gracefully

  • Merge multiple write_todos calls automatically
  • Or use the last value (as the name suggests)
  • Or provide a clear error message with recovery guidance

Option B: DeepAgents should prevent duplicate calls

  • Add middleware to deduplicate write_todos calls in the same step
  • Or provide clearer system prompts to prevent this behavior

Option C: Better documentation

  • Document this limitation clearly in DeepAgents/LangGraph docs
  • Provide best practices for coordinator agents
  • Add examples of proper todo management in parallel scenarios

🔧 Current Workarounds

Workaround 1: Retry Mechanism (Implemented)

async execute(input) {
  const MAX_RETRIES = 2;
  let attempt = 0;
  
  while (attempt <= MAX_RETRIES) {
    try {
      return await this.agent.invoke(input);
    } catch (error) {
      if (error.message?.includes('LastValue can only receive one value per step')) {
        if (attempt < MAX_RETRIES) {
          attempt++;
          console.warn(`⚠️  Detected concurrent update conflict, retrying (${attempt}/${MAX_RETRIES})...`);
          await new Promise(resolve => setTimeout(resolve, 1000));
          continue;
        }
      }
      throw error;
    }
  }
}

Pros:

  • Non-invasive (doesn't modify framework)
  • Works in ~90% of cases
  • Transparent to users

Cons:

  • Doesn't fix root cause
  • Adds latency (retry delay)
  • May still fail after retries

Workaround 2: Prompt Engineering

Add explicit constraints in the coordinator's system prompt:

<rules>
<critical>
- write_todos tool can only be called ONCE per step
- Create todos at the beginning, then issue all parallel task calls
- Do NOT call write_todos multiple times
</critical>
</rules>

Pros:

  • Reduces occurrence probability
  • No code changes needed

Cons:

  • LLM behavior is not 100% controllable
  • Exposes internal framework details to users
  • Doesn't guarantee prevention

📊 Impact

  • Severity: Medium-High
  • Frequency: Occurs in ~10-20% of parallel task workflows
  • Affected Users: Anyone using DeepAgents with coordinator + sub-agent patterns
  • Workaround Available: Yes (retry mechanism)

Real-World Test Results

In our test suite of 25 workflow scenarios:

  • L3-T1 (Multi-city weather comparison): Failed with this error
  • L3-T4 (Programming language comparison): ⚠️ Intermittent (passed on retry)
  • Other parallel workflows: ~10-20% failure rate

🎯 Proposed Solutions

Solution 1: Change todos Channel Type (Recommended)

// In LangGraph's channel configuration
const channels = {
  todos: new Reducer((prev, curr) => curr), // Take last value
  // Or
  todos: new Reducer((prev, curr) => {
    // Merge and deduplicate
    return curr; // Or merge logic
  }),
};

Impact: Low risk, high benefit

  • Fixes root cause
  • No breaking changes (behavior remains the same for single updates)
  • Enables more flexible todo management

Solution 2: Add Deduplication Middleware

// In DeepAgents
class TodoDeduplicationMiddleware {
  private todosCalledInStep = new Set<string>();
  
  async onToolStart(toolName: string, input: any, stepId: string) {
    if (toolName === 'write_todos') {
      if (this.todosCalledInStep.has(stepId)) {
        console.warn('⚠️  Skipping duplicate write_todos call in same step');
        return { skip: true };
      }
      this.todosCalledInStep.add(stepId);
    }
  }
  
  async onStepEnd(stepId: string) {
    this.todosCalledInStep.delete(stepId);
  }
}

Impact: Medium risk, high benefit

  • Prevents duplicate calls at middleware level
  • Transparent to users
  • ⚠️ Requires middleware support in LangGraph

Solution 3: Better Error Handling

// Provide actionable error messages
throw new InvalidUpdateError(
  'LastValue channel "todos" received multiple values in one step.\n' +
  'This usually happens when write_todos is called multiple times.\n' +
  'Possible solutions:\n' +
  '  1. Modify your prompt to call write_todos only once\n' +
  '  2. Use a Reducer channel instead of LastValue\n' +
  '  3. Implement retry logic in your application\n' +
  'See: https://docs.langchain.com/langgraph/channels'
);

Impact: Low risk, medium benefit

  • Helps users understand and fix the issue
  • No breaking changes
  • Doesn't prevent the error

Solution 4: Automatic Deduplication in LangGraph

// In LangGraph's tool execution logic
class ToolExecutor {
  async executeBatch(toolCalls: ToolCall[]) {
    // Deduplicate write_todos calls
    const deduped = this.deduplicateToolCalls(toolCalls);
    return await Promise.all(deduped.map(call => this.execute(call)));
  }
  
  private deduplicateToolCalls(toolCalls: ToolCall[]): ToolCall[] {
    const seen = new Set<string>();
    return toolCalls.filter(call => {
      if (call.name === 'write_todos') {
        if (seen.has('write_todos')) {
          console.warn('⚠️  Deduplicating write_todos call');
          return false;
        }
        seen.add('write_todos');
      }
      return true;
    });
  }
}

Impact: Low risk, high benefit

  • Transparent to users
  • Fixes the issue at framework level
  • No breaking changes

📚 Additional Context

Related Issues

  • Similar to concurrent state update issues in React/Redux
  • Related to LangGraph's channel design philosophy

Test Case Available

I can provide:

  • Complete workflow definition JSON
  • Full execution logs with timestamps
  • Reproducible test script
  • Analysis report with detailed breakdown

Why This Matters

DeepAgents is an amazing framework for building complex AI workflows. This issue is one of the few rough edges that affects production deployments. Fixing it would:

  • 🚀 Improve reliability for parallel workflows
  • 📈 Reduce user confusion and support burden
  • 💪 Make DeepAgents more robust for enterprise use

🙏 Request

Could the LangGraph team consider:

  1. Short-term: Improving error messages with actionable guidance
  2. Medium-term: Adding deduplication middleware or automatic deduplication
  3. Long-term: Changing the todos channel to use a more permissive reducer
  4. Documentation: Adding best practices for coordinator agents and parallel workflows

I'm happy to:

  • Provide more detailed test cases
  • Help test proposed solutions
  • Contribute a PR if guidance is provided

Thank you for your amazing work on LangGraph and DeepAgents! This framework has been incredibly valuable for building complex AI applications. 🚀


Environment Details:

  • Node.js: v18+
  • TypeScript: 5.x
  • @langchain/langgraph: Latest
  • @langchain/core: Latest
  • Test Date: November 9, 2025
Originally created by @supperdsj on GitHub (Nov 9, 2025). Original GitHub issue: https://github.com/langchain-ai/deepagentsjs/issues/52 # [Bug] InvalidUpdateError: LastValue channel cannot receive multiple values in same step when coordinator agent calls write_todos multiple times ## 🐛 Bug Description When a coordinator agent attempts to call `write_todos` multiple times within the same execution step (especially in parallel task scenarios), LangGraph throws an `InvalidUpdateError` due to the `LastValue` channel limitation. ### Error Message ``` InvalidUpdateError: Invalid update for channel "todos" with values [[{"content":"...","status":"in_progress"},...],[{"content":"...","status":"in_progress"},...]]: LastValue can only receive one value per step. Troubleshooting URL: https://docs.langchain.com/oss/javascript/langgraph/INVALID_CONCURRENT_GRAPH_UPDATE/ ``` ## 🔍 Environment - **Package**: `@langchain/langgraph` (JavaScript/TypeScript) - **Version**: Latest (as of Nov 2025) - **Framework**: DeepAgents workflow with coordinator + sub-agents pattern - **Runtime**: Node.js ## 📋 Steps to Reproduce ### Scenario A coordinator agent orchestrates multiple parallel sub-tasks (e.g., querying weather for multiple cities simultaneously). ### Workflow Structure ```typescript // Main coordinator agent const mainPrompt = ` You are a workflow coordinator. Task: Query weather for Beijing, Shanghai, and Shenzhen in parallel, then generate a comparison report. Execution Plan: 1. Parallel execution: - task('city_weather_searcher', 'Query Beijing weather') - task('city_weather_searcher', 'Query Shanghai weather') - task('city_weather_searcher', 'Query Shenzhen weather') 2. task('weather_comparator', 'Generate comparison report') `; // Sub-agents const subAgents = [ { id: 'city_weather_searcher', tools: ['web_search'] }, { id: 'weather_comparator', tools: [] } ]; ``` ### Execution Flow ``` Coordinator Agent Model Start ↓ Model End - Returns 8 tool calls: 1. write_todos (1st call) ← Creates todos 2. task('city_weather_searcher', 'Beijing') 3. task('city_weather_searcher', 'Shanghai') 4. task('city_weather_searcher', 'Shenzhen') 5. write_todos (2nd call) ← Duplicate! 🔥 6. task('city_weather_searcher', 'Beijing') 7. task('city_weather_searcher', 'Shanghai') 8. task('city_weather_searcher', 'Shenzhen') ↓ Execute tool calls ↓ ❌ InvalidUpdateError: LastValue channel conflict ``` ### Minimal Reproduction ```typescript import { createDeepAgent } from '@langchain/langgraph'; const agent = createDeepAgent({ mainPrompt: ` Coordinate parallel tasks: 1. Call task('worker', 'Task A') 2. Call task('worker', 'Task B') 3. Call task('worker', 'Task C') `, subAgents: [ { id: 'worker', systemPrompt: 'Execute the task', tools: [] } ] }); // This may trigger the error if LLM calls write_todos twice await agent.invoke({ messages: [{ role: 'user', content: 'Start' }] }); ``` ## 🎯 Root Cause 1. **LLM Behavior**: The coordinator agent's LLM generates duplicate `write_todos` calls in the same inference step, possibly because: - It interprets the parallel tasks as requiring separate todo lists - The prompt doesn't explicitly prohibit duplicate calls - The model's reasoning leads to redundant tool invocations 2. **LangGraph Limitation**: The `todos` channel uses `LastValue` reducer, which **cannot accept multiple values in a single step**. 3. **Trigger Condition**: This issue is more likely to occur when: - Multiple parallel tasks are involved (3+ parallel tasks) - The coordinator needs to orchestrate complex workflows - The prompt is ambiguous about todo management ## 💡 Expected Behavior One of the following should happen: **Option A (Preferred)**: LangGraph should handle multiple updates gracefully - Merge multiple `write_todos` calls automatically - Or use the last value (as the name suggests) - Or provide a clear error message with recovery guidance **Option B**: DeepAgents should prevent duplicate calls - Add middleware to deduplicate `write_todos` calls in the same step - Or provide clearer system prompts to prevent this behavior **Option C**: Better documentation - Document this limitation clearly in DeepAgents/LangGraph docs - Provide best practices for coordinator agents - Add examples of proper todo management in parallel scenarios ## 🔧 Current Workarounds ### Workaround 1: Retry Mechanism (Implemented) ```typescript async execute(input) { const MAX_RETRIES = 2; let attempt = 0; while (attempt <= MAX_RETRIES) { try { return await this.agent.invoke(input); } catch (error) { if (error.message?.includes('LastValue can only receive one value per step')) { if (attempt < MAX_RETRIES) { attempt++; console.warn(`⚠️ Detected concurrent update conflict, retrying (${attempt}/${MAX_RETRIES})...`); await new Promise(resolve => setTimeout(resolve, 1000)); continue; } } throw error; } } } ``` **Pros**: - ✅ Non-invasive (doesn't modify framework) - ✅ Works in ~90% of cases - ✅ Transparent to users **Cons**: - ❌ Doesn't fix root cause - ❌ Adds latency (retry delay) - ❌ May still fail after retries ### Workaround 2: Prompt Engineering Add explicit constraints in the coordinator's system prompt: ```xml <rules> <critical> - write_todos tool can only be called ONCE per step - Create todos at the beginning, then issue all parallel task calls - Do NOT call write_todos multiple times </critical> </rules> ``` **Pros**: - ✅ Reduces occurrence probability - ✅ No code changes needed **Cons**: - ❌ LLM behavior is not 100% controllable - ❌ Exposes internal framework details to users - ❌ Doesn't guarantee prevention ## 📊 Impact - **Severity**: Medium-High - **Frequency**: Occurs in ~10-20% of parallel task workflows - **Affected Users**: Anyone using DeepAgents with coordinator + sub-agent patterns - **Workaround Available**: Yes (retry mechanism) ### Real-World Test Results In our test suite of 25 workflow scenarios: - **L3-T1** (Multi-city weather comparison): ❌ Failed with this error - **L3-T4** (Programming language comparison): ⚠️ Intermittent (passed on retry) - **Other parallel workflows**: ~10-20% failure rate ## 🎯 Proposed Solutions ### Solution 1: Change `todos` Channel Type (Recommended) ```typescript // In LangGraph's channel configuration const channels = { todos: new Reducer((prev, curr) => curr), // Take last value // Or todos: new Reducer((prev, curr) => { // Merge and deduplicate return curr; // Or merge logic }), }; ``` **Impact**: Low risk, high benefit - ✅ Fixes root cause - ✅ No breaking changes (behavior remains the same for single updates) - ✅ Enables more flexible todo management ### Solution 2: Add Deduplication Middleware ```typescript // In DeepAgents class TodoDeduplicationMiddleware { private todosCalledInStep = new Set<string>(); async onToolStart(toolName: string, input: any, stepId: string) { if (toolName === 'write_todos') { if (this.todosCalledInStep.has(stepId)) { console.warn('⚠️ Skipping duplicate write_todos call in same step'); return { skip: true }; } this.todosCalledInStep.add(stepId); } } async onStepEnd(stepId: string) { this.todosCalledInStep.delete(stepId); } } ``` **Impact**: Medium risk, high benefit - ✅ Prevents duplicate calls at middleware level - ✅ Transparent to users - ⚠️ Requires middleware support in LangGraph ### Solution 3: Better Error Handling ```typescript // Provide actionable error messages throw new InvalidUpdateError( 'LastValue channel "todos" received multiple values in one step.\n' + 'This usually happens when write_todos is called multiple times.\n' + 'Possible solutions:\n' + ' 1. Modify your prompt to call write_todos only once\n' + ' 2. Use a Reducer channel instead of LastValue\n' + ' 3. Implement retry logic in your application\n' + 'See: https://docs.langchain.com/langgraph/channels' ); ``` **Impact**: Low risk, medium benefit - ✅ Helps users understand and fix the issue - ✅ No breaking changes - ❌ Doesn't prevent the error ### Solution 4: Automatic Deduplication in LangGraph ```typescript // In LangGraph's tool execution logic class ToolExecutor { async executeBatch(toolCalls: ToolCall[]) { // Deduplicate write_todos calls const deduped = this.deduplicateToolCalls(toolCalls); return await Promise.all(deduped.map(call => this.execute(call))); } private deduplicateToolCalls(toolCalls: ToolCall[]): ToolCall[] { const seen = new Set<string>(); return toolCalls.filter(call => { if (call.name === 'write_todos') { if (seen.has('write_todos')) { console.warn('⚠️ Deduplicating write_todos call'); return false; } seen.add('write_todos'); } return true; }); } } ``` **Impact**: Low risk, high benefit - ✅ Transparent to users - ✅ Fixes the issue at framework level - ✅ No breaking changes ## 📚 Additional Context ### Related Issues - Similar to concurrent state update issues in React/Redux - Related to LangGraph's channel design philosophy ### Test Case Available I can provide: - ✅ Complete workflow definition JSON - ✅ Full execution logs with timestamps - ✅ Reproducible test script - ✅ Analysis report with detailed breakdown ### Why This Matters DeepAgents is an amazing framework for building complex AI workflows. This issue is one of the few rough edges that affects production deployments. Fixing it would: - 🚀 Improve reliability for parallel workflows - 📈 Reduce user confusion and support burden - 💪 Make DeepAgents more robust for enterprise use ## 🙏 Request Could the LangGraph team consider: 1. **Short-term**: Improving error messages with actionable guidance 2. **Medium-term**: Adding deduplication middleware or automatic deduplication 3. **Long-term**: Changing the `todos` channel to use a more permissive reducer 4. **Documentation**: Adding best practices for coordinator agents and parallel workflows I'm happy to: - Provide more detailed test cases - Help test proposed solutions - Contribute a PR if guidance is provided Thank you for your amazing work on LangGraph and DeepAgents! This framework has been incredibly valuable for building complex AI applications. 🚀 --- **Environment Details**: - Node.js: v18+ - TypeScript: 5.x - @langchain/langgraph: Latest - @langchain/core: Latest - Test Date: November 9, 2025
yindo closed this issue 2026-02-16 06:16:48 -05:00
Author
Owner

@christian-bromann commented on GitHub (Jan 3, 2026):

Seems like this bug was fixed, will go ahead and close.

Thanks @supperdsj for raising, thanks @dqbd for fixing 🙌

@christian-bromann commented on GitHub (Jan 3, 2026): Seems like this bug was fixed, will go ahead and close. Thanks @supperdsj for raising, thanks @dqbd for fixing 🙌
yindo changed title from [Bug] InvalidUpdateError: LastValue channel cannot receive multiple values in same step when coordinator agent calls write_todos multiple times to [GH-ISSUE #52] [Bug] InvalidUpdateError: LastValue channel cannot receive multiple values in same step when coordinator agent calls write_todos multiple times 2026-06-05 17:20:57 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/deepagentsjs#13