langchain - ✅(Solved) Fix RunnableRetry.batch/abatch can return corrupted outputs when some items succeed on retry and others still fail [14 pull requests, 7 comments, 5 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#35475Fetched 2026-04-08 00:26:00
View on GitHub
Comments
7
Participants
5
Timeline
31
Reactions
0
Timeline (top)
cross-referenced ×14commented ×7referenced ×6labeled ×3
  • I'm using RunnableLambda(...).with_retry(...).batch(...) with return_exceptions=True.
  • I expect an input that still fails after all retry attempts to remain an exception in the matching output position.
  • Instead, if one item succeeds on retry while another still fails, the failing item can be replaced by the successful item's output.

Error Message

from langchain_core.runnables import RunnableLambda

failed_once = False

def process_item(name: str) -> str: global failed_once

if name == "ok":
    return "ok-result"
if name == "retry_then_ok":
    if not failed_once:
        failed_once = True
        raise ValueError()
    return "retry-result"
raise ValueError()

runnable = RunnableLambda(process_item).with_retry( stop_after_attempt=2, retry_if_exception_type=(ValueError,), wait_exponential_jitter=False, )

result = runnable.batch( ["ok", "retry_then_ok", "always_fail"], return_exceptions=True, )

Expected: the third item is an exception

print(result) assert isinstance(result[2], Exception)

Root Cause

  • I'm using RunnableLambda(...).with_retry(...).batch(...) with return_exceptions=True.
  • I expect an input that still fails after all retry attempts to remain an exception in the matching output position.
  • Instead, if one item succeeds on retry while another still fails, the failing item can be replaced by the successful item's output.

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 langgraph: 1.0.10 openai: 2.24.0 orjson: 3.11.7 packaging: 26.0 pydantic: 2.12.5 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 tenacity: 9.1.4 tiktoken: 0.12.0 typing-extensions: 4.15.0 uuid-utils: 0.14.1 xxhash: 3.6.0 zstandard: 0.25.0

PR fix notes

PR #35476: fix(core): RunnableRetry.batch corrupted outputs on partial retry success

Description (problem / solution / changelog)

Summary

Fixes #35475

RunnableRetry.batch() and abatch() return corrupted outputs when some items succeed on retry while others still fail after all attempts. A successful result appears in the output position of a permanently-failed item.

Root Cause

After retries exhaust, the output assembly loop (lines 282-288) uses result.pop(0) on the raw last-batch result list. But result still contains both successful outputs (already stored in results_map) and exceptions. Popping from the front returns a successful result instead of the expected exception.

Example: With inputs ["ok", "retry_then_ok", "always_fail"]:

  1. After final retry: result = ["retry-result", ValueError()]
  2. "retry-result" is already in results_map[1]
  3. For failed index 2: result.pop(0)"retry-result" ❌ (should be ValueError)

Fix

Filter the last-batch result to only contain exceptions before assembling the output list, since successful results are already captured in results_map:

exceptions = [r for r in result if isinstance(r, BaseException)]

Applied to both _batch() and _abatch().

Tests

Added 2 regression tests (test_retry_batch_mixed_success_and_permanent_failure + async variant) that reproduce the exact issue scenario. All 6 retry-related tests pass.

6 passed in 0.75s

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +8/-2)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +76/-0)

PR #35478: fix(core): prevent RunnableRetry.batch from returning corrupted outputs on partial failure

Description (problem / solution / changelog)

Summary

Fixes #35475

When using RunnableLambda(...).with_retry(...).batch(..., return_exceptions=True), if some items succeed on retry while others permanently fail, the failed items' positions in the output list can contain a successful result from a different item instead of the expected exception.

Root Cause

In both _batch() and _abatch(), after the retry loop completes:

  1. result holds the raw outputs from the last retry attempt (both successes and exceptions)
  2. Successful outputs from that attempt are added to results_map, but are not removed from result
  3. The output-assembly loop uses result.pop(0) for items not in results_map
  4. This pops a successful result (from a different item) instead of the exception for the permanently-failed item

Example with stop_after_attempt=2 and inputs ['ok', 'retry_then_ok', 'always_fail']:

  • After attempt 2: result = ['retry-result', ValueError()], results_map = {0: 'ok-result', 1: 'retry-result'}
  • For index 2 (not in results_map): result.pop(0) returns 'retry-result' instead of ValueError()

Fix

Filter result to keep only exceptions before the output-assembly loop:

result = [r for r in result if isinstance(r, Exception)]

This ensures each permanently-failed item gets its actual exception. Applied to both sync _batch and async _abatch.

Tests

Added:

  • test_retry_batch_no_corrupted_outputs_on_partial_failure (sync)
  • test_async_retry_batch_no_corrupted_outputs_on_partial_failure (async)

Both verify that the third item (always fails) is an exception while the first two items have their correct results. All 6 retry tests pass.

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +8/-0)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +76/-0)

PR #35485: fix(core): fix RunnableRetry.batch position corruption with return_exceptions

Description (problem / solution / changelog)

Summary

Fixes #35475

When batch() is called with return_exceptions=True and some items succeed on retry while others permanently fail, the output positions get corrupted. A retry-successful item can overwrite the exception position of a permanently-failed item.

Root Cause

The reconstruction loop at lines 282-287 used result.pop(0) to fill positions not in results_map, but result contained the full last-attempt output — including items that succeeded on retry and were already placed into results_map. Popping sequentially skipped those successes and pulled the wrong element for each remaining position.

Example

Given inputs ["ok", "retry_then_ok", "always_fail"] with stop_after_attempt=2:

  1. Attempt 1: ok succeeds → results_map[0]; retry_then_ok and always_fail fail
  2. Attempt 2: retry_then_ok succeeds → results_map[1]; always_fail still fails
  3. Reconstruction: Position 2 is not in results_map, so result.pop(0) is called — but result = ["retry-result", ValueError]. The pop returns "retry-result" instead of the ValueError, corrupting position 2.

Fix

Map the last-attempt results back to their original indices using remaining_indices, which correctly tracks the 1-to-1 correspondence between result offsets and original input positions. Both sync and async paths are fixed.

Tests

Added test_retry_batch_return_exceptions_position_integrity and its async variant, reproducing the exact scenario from the issue.

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +20/-14)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +74/-0)

PR #35503: fix(core): prevent RunnableRetry.batch output corruption when partial failures occur

Description (problem / solution / changelog)

Problem

RunnableRetry.batch/abatch can return corrupted outputs when some items succeed on retry while others fail. Results become misaligned with their original input indices.

Root Cause

In _batch and _abatch, output reconstruction uses result.pop(0) to assign results for items not yet in results_map. However, result contains ALL outputs from the last retry attempt—including successes already stored in results_map. When the last attempt has mixed results (e.g., [success, failure, success]), pop(0) consumes the wrong elements, misaligning outputs.

Example: 3 inputs all fail on attempt 1. On attempt 2, inputs 0 and 2 succeed while input 1 fails:

  • result = [ok_0, err_1, ok_2], results_map = {0: ok_0, 2: ok_2}
  • Reconstruction: idx 1 is not in results_map →

Fix

Replace pop-based reconstruction with index-mapped dict lookup using remaining_indices from the last retry iteration:

# Before (buggy)
outputs.append(result.pop(0))

# After (correct)
last_results = dict(zip(remaining_indices, result, strict=True))
outputs.append(last_results[idx])

This ensures each output is mapped back to its correct original index regardless of which items succeeded or failed in the last attempt.

Tests Added

7 new tests covering:

  • All succeed / all fail baselines
  • Partial failure with correct index alignment (the core bug)
  • Mixed last-attempt results (specifically triggers the pop corruption)
  • Staggered retries across multiple attempts
  • Async variants of the corruption scenarios

Fixes #35475

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +12/-2)
  • libs/core/tests/unit_tests/runnables/test_runnable_retry_batch.py (added, +163/-0)

PR #35508: fix: preserve correct index mapping in RunnableRetry.batch - Fixes #35475

Description (problem / solution / changelog)

Summary

Fixes issue #35475 - RunnableRetry.batch can return corrupted outputs when some items succeed on retry and others still fail.

Problem

When using batch() with retry and return_exceptions=True, if some items succeed on retry while others still fail, the output indices could get mixed up. This was because result.pop(0) was being used without proper tracking of which results belong to which indices.

Solution

Changed the output construction to properly track remaining results:

# Before (buggy):
outputs.append(result.pop(0))

# After (fixed):
remaining_results = list(result) if result != not_set else []
for idx in range(len(inputs)):
    if idx in results_map:
        outputs.append(results_map[idx])
    elif remaining_results:
        outputs.append(remaining_results.pop(0))

This ensures exceptions are mapped to their correct original indices.

Changes

  • libs/core/langchain_core/runnables/retry.py: Fixed both sync _batch and async _abatch methods

AI-assisted contribution (built with assistance)

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +18/-2)

PR #35556: fix: RunnableRetry.batch/abatch can return corrupted outputs when s...

Description (problem / solution / changelog)

Summary

Fixes #35475

Changes

Auto-generated contribution addressing: RunnableRetry.batch/abatch can return corrupted outputs when some items succeed on retry and others still fail

Testing

  • Ran the project's test suite
  • Verified the fix addresses the issue

Contributed via automated open-source bot

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +10/-2)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +79/-0)

PR #35600: fix(core): correctly map failed results in RunnableRetry batch/abatch

Description (problem / solution / changelog)

Summary

Fixes #35475.

  • `_batch` and `_abatch` used `result.pop(0)` to assign outputs for inputs that exhausted all retries
  • `result` is indexed by position in `remaining_indices` from the last retry attempt, not by original input index
  • When some inputs succeed on the final retry attempt and others fail, `result` contains more entries than there are failures — `pop(0)` picks up the wrong values (a succeeded value gets assigned to a failed slot)

Fix

Two minimal changes in both `_batch` and `_abatch`:

  1. Initialize `remaining_indices` before the `try` block (ensures it is always defined even if the loop body never runs, e.g. empty inputs)
  2. Replace `result.pop(0)` with `result[remaining_indices.index(idx)]` — maps each failed original index back to its correct position in the last batch result using `remaining_indices` which is still in scope after the loop

Comparison with other open PRs for #35475

There are 3 other PRs targeting the same issue. Here is why they are incorrect or have gaps:

PRApproachProblem
#35556`zip(failed_indices, result, strict=True)`Crashes with `ValueError`: `failed_indices` contains only indices not in `results_map`, but `result` contains entries for ALL of `remaining_indices` from the last attempt (including items that just succeeded). Lengths differ → `strict=True` raises.
#35508`remaining_results = list(result); remaining_results.pop(0)`Still wrong: this is just a copy of `result` with the same `pop(0)` misalignment — it doesn't fix the root cause.
#35503`dict(zip(remaining_indices, result, strict=True))`Logically correct. Uses the same insight as this PR (map via `remaining_indices`), but builds a full intermediate dict. Our approach uses `remaining_indices.index(idx)` inline without allocating an extra dict.

Test plan

  • `test_retry_batch_partial_success_with_persistent_failure` — main regression: middle item succeeds on retry, last item always fails → previously the last slot received the retry-success value instead of the exception
  • `test_retry_batch_failure_at_first_position` — failure at index 0 to catch reverse-order corruption
  • Async variants of both tests (`abatch`)
  • All 1704 existing `langchain-core` unit tests pass

🤖 This PR was developed with assistance from Claude Code (claude-sonnet-4-6).

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +8/-2)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +146/-0)
  • libs/langchain_v1/langchain/agents/middleware/tool_selection.py (modified, +18/-5)
  • libs/langchain_v1/tests/unit_tests/agents/middleware/implementations/test_tool_selection.py (modified, +80/-0)
  • libs/text-splitters/langchain_text_splitters/konlpy.py (modified, +7/-13)
  • libs/text-splitters/langchain_text_splitters/nltk.py (modified, +4/-9)
  • libs/text-splitters/langchain_text_splitters/sentence_transformers.py (modified, +7/-12)
  • libs/text-splitters/langchain_text_splitters/spacy.py (modified, +12/-15)
  • libs/text-splitters/tests/unit_tests/test_text_splitters.py (modified, +20/-0)

PR #35602: fix(core): RunnableRetry batch/abatch corrupted outputs

Description (problem / solution / changelog)

Found this bug while going through open issues and wanted to give it a shot.

The issue was in _batch and _abatch - when retrying, some inputs succeed and some fail. The old code used result.pop(0) for failed outputs but result only had the failing items, so outputs came out wrong/corrupted.

Fixed it by tracking failed indices in an exceptions_map (same pattern as results_map for successes) so the final output uses the right value for each index.

Fixes #35475 LinkedIn: https://www.linkedin.com/in/sandeshbhandari171/ Note: used AI assistance to help debug and understand this more clearly and then I fixed this.

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +31/-21)

PR #35622: fix(core): prevent output corruption in RunnableRetry.batch with return_exceptions

Description (problem / solution / changelog)

Bug

RunnableRetry.batch() (and .abatch()) can return corrupted outputs when some items succeed on retry while others still fail and return_exceptions=True is set.

Reported in: #35475

Root cause

After retries, the final assembly loop used result.pop(0) to fill positions of items not in results_map. However, result still contained the successfully-retried values alongside the exceptions. The pop consumed the wrong elements, replacing exceptions with stale success values.

Walkthrough (from the issue reproducer):

  1. Attempt 1: batch ["ok", "retry_then_ok", "always_fail"]["ok-result", ValueError, ValueError]. "ok-result" saved to results_map[0].
  2. Attempt 2: batch ["retry_then_ok", "always_fail"]result = ["retry-result", ValueError]. "retry-result" saved to results_map[1].
  3. Assembly: for idx=2 (not in results_map): result.pop(0)pops "retry-result" instead of the ValueError.

Fix

Replace the result.pop(0) assembly with an index-mapped lookup. After the retry loop, last_remaining_indices records which original positions the last batch call corresponded to, allowing direct lookup of each position's result without the ordering bug.

Tests

  • Added test_retry_batch_return_exceptions_no_corruption (sync)
  • Added test_async_retry_batch_return_exceptions_no_corruption (async)
  • All 6 retry tests pass with no regressions

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +26/-4)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +76/-0)

PR #35656: core: fix RunnableRetry batch output corruption when partial retries succeed

Description (problem / solution / changelog)

Description

Fixes a bug where RunnableRetry.batch() and abatch() return corrupted outputs when some items succeed on retry while others exhaust all retry attempts.

Root cause

After retries exhaust, the output assembly loop used result.pop(0) to assign remaining results to output positions not in results_map. However, result (from the last retry attempt) contains entries for all remaining indices — including items that succeeded on that last attempt and were already added to results_map. The pop(0) consumed those successful entries, causing failed items to receive the wrong output instead of their exception.

Example: With inputs ["ok", "retry_then_ok", "always_fail"] and stop_after_attempt=2:

  • After attempt 1: results_map = {0: "ok-result"}, remaining = [1, 2]
  • After attempt 2: results_map = {0: "ok-result", 1: "retry-result"}, result = ["retry-result", ValueError]
  • Output assembly: idx=2 gets result.pop(0)"retry-result" (WRONG, should be ValueError)

Fix

Replace result.pop(0) with an explicit mapping from remaining indices to their result entries, correctly associating each output position with its value or exception.

Issue

Fixes #35475

Tests

Verified with the reproduction script from the issue — all assertions pass.

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +34/-2)

PR #35671: fix(core): track per-index exceptions in RunnableRetry batch to prevent output corruption

Description (problem / solution / changelog)

Summary

RunnableRetry._batch (and _abatch) silently returned wrong outputs when a batch contained a mix of inputs — some that succeed after retry and some that exhaust all attempts and should return exceptions.

Root Cause

The fallback output-reconstruction loop called result.pop(0) on the last pending batch result for every index not in results_map. Once the pending list shrank on a retry (because some items succeeded), the positions in that list no longer matched original indices. Popping sequentially then replaced an expected exception with a value from a different input.

Minimal reproduction (from #35475):

result = runnable.batch(["ok", "retry_then_ok", "always_fail"], return_exceptions=True)
# Before fix: result[2] could be "retry-result" instead of an Exception
# After fix:  result[2] is always the ValueError("always fails")

Fix

Maintain a parallel exceptions_map: dict[int, Exception] alongside results_map. On every attempt, each exception is stored keyed by its original input index (and cleared if a later attempt succeeds for that index). The output-reconstruction loop checks exceptions_map before falling back to result.pop(0), so the fallback is only reached when the initial sentinel is still in place (i.e. the very first attempt raised without returning any output at all).

Same change applied symmetrically to _abatch.

Tests Added

  • test_retry_batch_return_exceptions_mixed — exact repro from #35475
  • test_async_retry_batch_return_exceptions_mixed — async variant

Both added to libs/core/tests/unit_tests/runnables/test_runnable.py.

Fixes #35475

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +22/-8)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +72/-0)

PR #35683: fix(core): prevent output corruption in RunnableRetry.batch when partial retries succeed

Description (problem / solution / changelog)

Bug

RunnableRetry.batch() / abatch() with return_exceptions=True can return corrupted outputs when some items succeed on retry while others still fail. A permanently-failing item can be silently replaced by a successfully-retried value from a different position.

Root Cause

After retries exhaust, the final assembly loop uses result.pop(0) to fill positions not yet in results_map. But result still contains all items from the last retry batch — including successfully-retried values already saved to results_map. The pop(0) consumes them in order, picking up the wrong element for positions that should be exceptions.

Example (from the issue):

  • Inputs: ["ok", "retry_then_ok", "always_fail"]
  • After attempt 2, result = ["retry-result", ValueError]
  • results_map = {0: "ok-result", 1: "retry-result"}
  • For index 2 (not in map): result.pop(0) returns "retry-result" instead of the ValueError

Fix

Replace the pop(0)-based assembly with an index-mapped lookup:

  • Track last_remaining_indices across retry iterations
  • After the loop, build last_result_map = dict(zip(last_remaining_indices, result))
  • Look up each original index in either results_map (succeeded) or last_result_map (still-failing)

Applied identically to both _batch and _abatch.

Tests

Added sync and async regression tests that reproduce the exact scenario from the issue: one item succeeds immediately, one succeeds on retry, one always fails. Before this fix, result[2] was "retry-result" instead of an exception.

Fixes #35475

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +26/-2)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +71/-0)

PR #35715: fix(core): use index-based result mapping in RunnableRetry batch methods

Description (problem / solution / changelog)

Description

Fixes #35475

RunnableRetry._batch() and _abatch() used result.pop(0) to assign exception outputs to inputs that hadn't succeeded. This produced corrupted results when the last retry attempt had a mix of successes and failures: successful items got added to results_map but their entries stayed in the result list, so subsequent .pop(0) calls returned the wrong values.

For example with inputs [A, B, C, D] where A fails then succeeds on retry while C always fails:

  • Last batch result = [success_A, exception_C]
  • success_A gets added to results_map, but stays in result
  • Final assembly: D (not in results_map) pops success_A instead of getting its own result

Changes

  • Replaced the result/not_set/pop(0) pattern with an exceptions_map dict that tracks failed results by their original input index
  • Final assembly looks up each index in results_map (successes) then exceptions_map (failures), avoiding positional assumptions
  • Applied the same fix to both _batch() and _abatch()
  • Removed now-unused cast import

Tests

  • Added test_retry_batch_mixed_success_failure and its async variant
  • Test scenario: 4 inputs where one has a transient failure (succeeds on retry), one always fails, two always succeed
  • Verifies each output maps to the correct original input index

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +28/-28)
  • libs/core/tests/unit_tests/runnables/test_runnable.py (modified, +61/-0)

PR #35744: fix(core): prevent output corruption in RunnableRetry.batch when partial retries succeed

Description (problem / solution / changelog)

Summary

  • Fixes _batch and _abatch in RunnableRetry where result.pop(0) on the shortened last-retry result list caused output misalignment.
  • When some inputs succeed on earlier retries and are removed from the pending list, the final result list is shorter than the original input list. Using pop(0) assigned successful results from result to failed indices in the output.
  • The fix uses the caught RetryError exception for any index not in results_map, ensuring failed inputs get the error and successful inputs keep their correct results.
  • Also removes the unused not_set sentinel variable.

Fixes #35475

Test plan

  • Verify existing retry batch tests pass
  • Test scenario: batch of 3 inputs where input 0 succeeds on attempt 1, input 1 fails all retries, input 2 succeeds on attempt 2 - verify outputs[0] and outputs[2] are correct results, outputs[1] is RetryError

This PR was developed with AI agent assistance.

Changed files

  • libs/core/langchain_core/runnables/retry.py (modified, +6/-10)

Code Example

from langchain_core.runnables import RunnableLambda


failed_once = False


def process_item(name: str) -> str:
    global failed_once

    if name == "ok":
        return "ok-result"
    if name == "retry_then_ok":
        if not failed_once:
            failed_once = True
            raise ValueError()
        return "retry-result"
    raise ValueError()


runnable = RunnableLambda(process_item).with_retry(
    stop_after_attempt=2,
    retry_if_exception_type=(ValueError,),
    wait_exponential_jitter=False,
)

result = runnable.batch(
    ["ok", "retry_then_ok", "always_fail"],
    return_exceptions=True,
)

# Expected: the third item is an exception
print(result)
assert isinstance(result[2], Exception)

---
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

from langchain_core.runnables import RunnableLambda


failed_once = False


def process_item(name: str) -> str:
    global failed_once

    if name == "ok":
        return "ok-result"
    if name == "retry_then_ok":
        if not failed_once:
            failed_once = True
            raise ValueError()
        return "retry-result"
    raise ValueError()


runnable = RunnableLambda(process_item).with_retry(
    stop_after_attempt=2,
    retry_if_exception_type=(ValueError,),
    wait_exponential_jitter=False,
)

result = runnable.batch(
    ["ok", "retry_then_ok", "always_fail"],
    return_exceptions=True,
)

# Expected: the third item is an exception
print(result)
assert isinstance(result[2], Exception)

Error Message and Stack Trace (if applicable)

Description

  • I'm using RunnableLambda(...).with_retry(...).batch(...) with return_exceptions=True.
  • I expect an input that still fails after all retry attempts to remain an exception in the matching output position.
  • Instead, if one item succeeds on retry while another still fails, the failing item can be replaced by the successful item's output.

System Info

System Information

OS: Linux OS Version: #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025 Python Version: 3.10.12 (main, Jan 26 2026, 14:55:28) [GCC 11.4.0]

Package Information

langchain_core: 1.2.16 langchain: 1.2.10 langsmith: 0.7.9 langchain_openai: 1.1.10 langgraph_sdk: 0.3.9

Optional packages not installed

deepagents deepagents-cli

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 langgraph: 1.0.10 openai: 2.24.0 orjson: 3.11.7 packaging: 26.0 pydantic: 2.12.5 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 tenacity: 9.1.4 tiktoken: 0.12.0 typing-extensions: 4.15.0 uuid-utils: 0.14.1 xxhash: 3.6.0 zstandard: 0.25.0

extent analysis

Fix Plan

1. Update langchain_core to the latest version

Update langchain_core to the latest version (1.2.17 or higher) to ensure you have the latest retry logic.

pip install --upgrade langchain_core

2. Update langchain to the latest version

Update langchain to the latest version (1.2.11 or higher) to ensure you have the latest retry logic.

pip install --upgrade langchain

3. Update langsmith to the latest version

Update langsmith to the latest version (0.7.10 or higher) to ensure you have the latest retry logic.

pip install --upgrade langsmith

4. Update langchain_openai to the latest version

Update langchain_openai to the latest version (1.1.11 or higher) to ensure you have the latest retry logic.

pip install --upgrade langchain_openai

5. Update langgraph_sdk to the latest version

Update langgraph_sdk to the latest version (0.3.10 or higher) to ensure you have the latest retry logic.

pip install --upgrade langgraph_sdk

6. Update langchain_core to use the wait_exponential_jitter=True option

Update your code to use the wait_exponential_jitter=True option when creating the RunnableLambda instance.

runnable = RunnableLambda(process_item).with_retry(
    stop_after_attempt=2,
    retry_if_exception_type=(ValueError,),
    wait_exponential_jitter=True,
)

7. Test your code

Test your code with the updated dependencies and retry logic to ensure it works as expected.

result = runnable

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING