[PR #6633] fix(langgraph): generate unique interrupt IDs for parallel interrupts #5173

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

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

State: closed
Merged: No


Summary

Fixes #6626 - interrupt() calls in parallel tools generate identical IDs, making multi-interrupt resume impossible.

Problem

When multiple tools ran in parallel within the same ToolNode (or a single node called interrupt() multiple times), all interrupts received identical IDs. This happened because the interrupt ID was computed as hash(namespace) without considering the interrupt index.

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

  1. langgraph/types.py

    • Interrupt.from_ns() now accepts idx parameter
    • ID format: {hash(namespace)}:{idx}
    • interrupt() function passes idx from scratchpad counter
  2. 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
  3. 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
  • Serialization roundtrip tests verify legacy interrupt deserialization

Test plan

Added comprehensive tests:

  • 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 test_parallel_interrupts and test_parallel_interrupts_double continue to pass
  • Serialization roundtrip tests for legacy interrupt format
# Run all interrupt-related tests
TEST="tests/test_pregel.py::test_parallel_interrupts tests/test_pregel.py::test_parallel_interrupts_double tests/test_interruption.py tests/test_interrupt_migration.py -v" make test
# Result: 31 passed

Related Issues

  • This fix addresses #6626
  • Related to #6624 (ToolNode doesn't collect all interrupts from parallel execution) - that's a separate issue about interrupt collection, not ID uniqueness
**Original Pull Request:** https://github.com/langchain-ai/langgraph/pull/6633 **State:** closed **Merged:** No --- ## Summary Fixes #6626 - `interrupt()` calls in parallel tools generate identical IDs, making multi-interrupt resume impossible. ### Problem When multiple tools ran in parallel within the same `ToolNode` (or a single node called `interrupt()` multiple times), all interrupts received **identical IDs**. This happened because the interrupt ID was computed as `hash(namespace)` without considering the interrupt index. **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 1. **`langgraph/types.py`** - `Interrupt.from_ns()` now accepts `idx` parameter - ID format: `{hash(namespace)}:{idx}` - `interrupt()` function passes `idx` from scratchpad counter 2. **`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 3. **`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 - Serialization roundtrip tests verify legacy interrupt deserialization ## Test plan Added comprehensive tests: - [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 `test_parallel_interrupts` and `test_parallel_interrupts_double` continue to pass - [x] Serialization roundtrip tests for legacy interrupt format ```bash # Run all interrupt-related tests TEST="tests/test_pregel.py::test_parallel_interrupts tests/test_pregel.py::test_parallel_interrupts_double tests/test_interruption.py tests/test_interrupt_migration.py -v" make test # Result: 31 passed ``` ## Related Issues - This fix addresses #6626 - Related to #6624 (ToolNode doesn't collect all interrupts from parallel execution) - that's a separate issue about interrupt collection, not ID uniqueness
yindo added the pull-request label 2026-02-20 17:51:20 -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#5173