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

ModelRouterMiddleware Raises Opaque RuntimeError When State Has No Human Message

ModelRouterMiddleware._latest_human_message uses next() without a default, causing StopIteration to be converted into an opaque RuntimeError on both sync and async paths when no HumanMessage exists in the state. The error does not identify the middleware or the remedy.

mediumConfidence 96%LangChainAffected Vmaster

Origin Analysis

In langchain-typesafe.experimental.middleware.ModelRouterMiddleware, _latest_human_message calls next() on a filtered reversed generator; when no HumanMessage is present, the generator is exhausted and PEP 479 converts StopIteration into an opaque RuntimeError instead of a descriptive error.
Run the provided Python snippet with inputs = {'messages': [AIMessage('Resumed without a user turn')]}. The sync invocation raises RuntimeError: generator raised StopIteration; the async invocation raises RuntimeError: coroutine raised StopIteration.

Fixing Code Block

from langchain_core.messages import HumanMessage def _latest_human_message(self, state): messages = state.get("messages") or [] human_message = next( (msg for msg in reversed(messages) if isinstance(msg, HumanMessage)), None, ) if human_message is None: raise ValueError( "ModelRouterMiddleware requires a HumanMessage in the state to route, " "but none was found. Provide a user message or enter the graph from a user turn." ) return human_message
The fix fetches the latest HumanMessage using next(..., None), explicitly checks for None, and raises a ValueError with clear middleware and state context. This matches the package's documented ValueError behavior for invalid configuration and prevents any classifier request from being sent.

Edge Case Audit

Switching from RuntimeError to ValueError may break callers that catch RuntimeError. Review exception handling around agent.invoke/ainvoke. If state.messages is not a list (e.g., a custom reducer returning a non-iterable), the `or []` guard plus reversed may still fail; ensure messages is iterable. Rollback: restore the previous next(generator) implementation and re-test all affected flows.

Ecosystem Topology