[PR #6627] fix(langgraph): generate unique interrupt IDs for parallel tool execution #5169

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

Original Pull Request: https://github.com/langchain-ai/langgraph/pull/6627

State: closed
Merged: No


Fix for #6626

Enabling proper interrupt handling when multiple tools run in parallel.

Description

Updated Interrupt handling mechanism to support multiple tools running in parallel by generating unique interrupt IDs for each concurrent execution.

When multiple tools execute in parallel with interrupts, the resume mechanism fails because all interrupts share the same ID. This prevents proper handling of Command(resume={id1: response1, id2: response2, id3: response3}).

Example of the bug:

  # Three parallel tool calls, each with interrupt()
  Interrupts collected: 3
    0: id=1b09852344aeb4aee6776a24d09c3f9e, tool=create_circle
    1: id=1b09852344aeb4aee6776a24d09c3f9e, tool=create_square  # Same ID!
    2: id=1b09852344aeb4aee6776a24d09c3f9e, tool=create_triangle # Same ID!
  # Resume dict collapses due to key collision:
  resume_data = {intr.id: value for intr in interrupts}  # Only 1 entry!

Solution

Changed the interrupt ID format to include the interrupt index:

Before After
a61a86efc1202990625a73c5591eee22 a61a86efc1202990625a73c5591eee22:0
(same for all interrupts in namespace) (unique per interrupt: :0, :1, :2)

Changes

  • langgraph/types.py
    • Interrupt.from_ns() now accepts idx parameter
    • ID format: {hash(namespace)}:{idx}
    • interrupt() function passes idx from scratchpad counter
  • langgraph/pregel/_algo.py
    • _scratchpad() updated to match resume values with new ID format
    • Maintains backward compatibility with old-format IDs
    • Iterates through {namespace_hash}:{idx} entries in resume_map
  • langgraph/pregel/_utils.py
    • is_xxh3_128_hexdigest() regex updated to match both formats:
      • Legacy: [0-9a-f]{32}
      • New: [0-9a-f]{32}:\d+

Backward Compatibility

  • Old checkpoints with legacy interrupt IDs continue to work
  • The _scratchpad() function checks for both old and new formats

Test Plan

  • test_from_ns_unique_ids_for_different_indices - Unit test verifying unique IDs
  • test_multiple_interrupts_in_node_have_unique_ids - Sequential interrupts in single node
  • test_parallel_tool_interrupts_have_unique_ids - Integration test with parallel Send() dispatches
  • Existing tests continue to pass
  • Format and lint checks pass

Manual verification tests passed:

  • Interrupt IDs now have format {hash}:{idx}
  • Multiple interrupts in same node get unique IDs
  • Parallel tools via Send() get unique IDs
  • Resume with Command(resume={id: value}) works correctly

Breaking Changes

None. This change is fully backward compatible.

**Original Pull Request:** https://github.com/langchain-ai/langgraph/pull/6627 **State:** closed **Merged:** No --- Fix for #6626 Enabling proper interrupt handling when multiple tools run in parallel. ## Description Updated Interrupt handling mechanism to support multiple tools running in parallel by generating unique interrupt IDs for each concurrent execution. When multiple tools execute in parallel with interrupts, the resume mechanism fails because all interrupts share the same ID. This prevents proper handling of `Command(resume={id1: response1, id2: response2, id3: response3})`. **Example of the bug:** ```python # Three parallel tool calls, each with interrupt() Interrupts collected: 3 0: id=1b09852344aeb4aee6776a24d09c3f9e, tool=create_circle 1: id=1b09852344aeb4aee6776a24d09c3f9e, tool=create_square # Same ID! 2: id=1b09852344aeb4aee6776a24d09c3f9e, tool=create_triangle # Same ID! # Resume dict collapses due to key collision: resume_data = {intr.id: value for intr in interrupts} # Only 1 entry! ``` ### Solution Changed the interrupt ID format to include the interrupt index: | Before | After | |----------------------------------------|------------------------------------| | a61a86efc1202990625a73c5591eee22 | a61a86efc1202990625a73c5591eee22:0 | | (same for all interrupts in namespace) | (unique per interrupt: :0, :1, :2) | ### Changes - langgraph/types.py - Interrupt.from_ns() now accepts idx parameter - ID format: {hash(namespace)}:{idx} - interrupt() function passes idx from scratchpad counter - langgraph/pregel/_algo.py - _scratchpad() updated to match resume values with new ID format - Maintains backward compatibility with old-format IDs - Iterates through {namespace_hash}:{idx} entries in resume_map - langgraph/pregel/_utils.py - is_xxh3_128_hexdigest() regex updated to match both formats: - Legacy: [0-9a-f]{32} - New: [0-9a-f]{32}:\d+ ### Backward Compatibility - Old checkpoints with legacy interrupt IDs continue to work - The _scratchpad() function checks for both old and new formats ## Test Plan - [x] test_from_ns_unique_ids_for_different_indices - Unit test verifying unique IDs - [x] test_multiple_interrupts_in_node_have_unique_ids - Sequential interrupts in single node - [x] test_parallel_tool_interrupts_have_unique_ids - Integration test with parallel Send() dispatches - [x] Existing tests continue to pass - [x] Format and lint checks pass ### Manual verification tests passed: - [x] Interrupt IDs now have format {hash}:{idx} - [x] Multiple interrupts in same node get unique IDs - [x] Parallel tools via Send() get unique IDs - [x] Resume with Command(resume={id: value}) works correctly ## Breaking Changes None. This change is fully backward compatible.
yindo added the pull-request label 2026-02-20 17:51:19 -05:00
yindo closed this issue 2026-02-20 17:51:20 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#5169