RunnableAssign leaves mapper work running after passthrough input validation fails
Author: Iris070119Created Sep 16, 2026Updated Sep 17, 2026
Labelsbugcoreexternal
Submission checklist
- 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)
import asyncio
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
async def main() -> None:
mapper_started = asyncio.Event()
mapper_cancelled = False
async def slow_mapper(value):
nonlocal mapper_cancelled
mapper_started.set()
try:
await asyncio.sleep(60)
return "finished"
except asyncio.CancelledError:
mapper_cancelled = True
raise
runnable = RunnablePassthrough.assign(
mapped=RunnableLambda(slow_mapper),
)
async def invalid_input():
yield {"valid": "first"}
await mapper_started.wait()
# RunnablePassthrough.assign() only accepts dictionaries.
yield "invalid input"
try:
async for _ in runnable.atransform(invalid_input()):
pass
except ValueError as error:
print("input error:", error)
await asyncio.sleep(0)
current_task = asyncio.current_task()
pending_tasks = [
task
for task in asyncio.all_tasks()
if task is not current_task and not task.done()
]
print("pending background tasks:", len(pending_tasks))
print("mapper cancelled:", mapper_cancelled)
# Cleanup for this reproduction script.
for task in pending_tasks:
task.cancel()
await asyncio.gather(*pending_tasks, return_exceptions=True)
asyncio.run(main())
Error Message and Stack Trace (if applicable)
input error: The input to RunnablePassthrough.assign() must be a dict.
pending background tasks: 4
mapper cancelled: False
Description
RunnableAssign._atransform() starts the mapper in a background task before it
finishes validating all passthrough input chunks.
If a later input chunk is not a dictionary, the passthrough loop raises
ValueError. The method exits without cancelling and awaiting the already
started mapper task.
The relevant control flow is:
first_map_chunk_task = asyncio.create_task(
anext(map_output, None),
)
async for chunk in for_passthrough:
if not isinstance(chunk, dict):
msg = "The input to RunnablePassthrough.assign() must be a dict."
raise ValueError(msg)
first_map_chunk = await first_map_chunk_task
### System Info
System Information
------------------
> OS: Windows
> OS Version: 10.0.26200
> Python Version: 3.13.3 (main, May 30 2025, 05:37:00) [MSC v.1943 64 bit (AMD64)]
Package Information
-------------------
> langchain_core: 1.6.3
> langsmith: 0.12.5
> langchain_classic: 1.0.8
> langchain_huggingface: 1.2.2
> langchain_protocol: 0.0.19
> langchain_text_splitters: 1.1.2
Optional packages not installed
-------------------------------
> deepagents
> deepagents-cli
Other Dependencies
------------------
> anyio: 4.15.1
> distro: 1.9.0
> httpx: 0.28.1
> httpx2: 2.13.0
> huggingface-hub: 1.31.0
> jsonpatch: 1.33
> orjson: 3.12.0
> packaging: 26.3
> pydantic: 2.13.5
> pyyaml: 6.0.3
> requests: 2.34.2
> requests-toolbelt: 1.0.0
> sniffio: 1.3.1
> sqlalchemy: 2.0.54
> tenacity: 9.1.4
> tokenizers: 0.23.2
> typing-extensions: 4.16.0
> uuid-utils: 0.17.1
> websockets: 17.1
> xxhash: 4.0.1
> zstandard: 0.25.0
### Social handles (optional)
_No response_
Source: langchain-ai/langchain