AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

ContextThreadPoolExecutor.Map Rejects Generator Inputs

ContextThreadPoolExecutor.map() calls len(iterables[0]) to preallocate contexts, which raises TypeError for generators and other unsized iterables, even though the standard ThreadPoolExecutor.map() accepts them. The proposed fix delegates directly to super().map(), relying on the existing submit() override for context propagation.

mediumConfidence 95%Langchain-CoreAffected V1.5.3

Origin Analysis

The override preallocates one context per item using range(len(iterables[0])), assuming the first iterable is sized. This duplicates responsibility already handled by ContextThreadPoolExecutor.submit() and incorrectly rejects valid unsized iterables.
Run the following Python code: ```python from concurrent.futures import ThreadPoolExecutor from langchain_core.runnables.config import ContextThreadPoolExecutor def values(): yield from range(3) with ContextThreadPoolExecutor(max_workers=2) as executor: print(list(executor.map(lambda value: value * 2, values()))) ``` Observe TypeError: object of type 'generator' has no len().

Fixing Code Block

def map( self, fn: Callable[..., T], *iterables: Iterable[Any], **kwargs: Any, ) -> Iterator[T]: return super().map(fn, *iterables, **kwargs)
The method delegates to the standard ThreadPoolExecutor.map(). The standard implementation internally calls self.submit() for each item. Because ContextThreadPoolExecutor overrides submit() to wrap the function with copy_context().run(...), context propagation is preserved automatically. This removes the problematic len() call and restores support for generators, lazy iterables, multiple iterables, and standard map keyword arguments.

Edge Case Audit

This fix relies on the standard library's ThreadPoolExecutor.map() using self.submit() internally. In CPython 3.12 and typical versions this is true, but if a future Python version changes the implementation to bypass dynamic dispatch, context propagation could silently break. Additionally, removing eager preallocation may slightly change timing for context creation but should not affect correctness. If issues arise, rollback to the previous approach and instead guard len() by converting the first iterable to a list only when needed, or use itertools.tee to preserve laziness.

Ecosystem Topology