langchain-ai/langchain · error · ValueError
Expected generate to return a ChatResult, but got {type(chat
Error message
Expected generate to return a ChatResult, but got {type(chat_result)} instead. What it means
`ValueError` in `FakeChatModel`-family `_stream`: `_generate` returned something that is not a `ChatResult`. The fake bridging code calls the model's own `_generate` and slices `chat_result.generations[0].message`, so it hard-requires the documented `ChatResult` return type; a custom subclass returning e.g. a message or string breaks it.
Source
Thrown at libs/core/langchain_core/language_models/fake_chat_models.py:281
generation = ChatGeneration(message=message_)
return ChatResult(generations=[generation])
def _stream(
self,
messages: list[BaseMessage],
stop: list[str] | None = None,
run_manager: CallbackManagerForLLMRun | None = None,
**kwargs: Any,
) -> Iterator[ChatGenerationChunk]:
chat_result = self._generate(
messages, stop=stop, run_manager=run_manager, **kwargs
)
if not isinstance(chat_result, ChatResult):
msg = ( # type: ignore[unreachable]
f"Expected generate to return a ChatResult, "
f"but got {type(chat_result)} instead."
)
raise ValueError(msg) # noqa: TRY004
message = chat_result.generations[0].message
if not isinstance(message, AIMessage):
msg = (
f"Expected invoke to return an AIMessage, "
f"but got {type(message)} instead."
)
raise ValueError(msg) # noqa: TRY004
content = message.content
if content:
# Use a regular expression to split on whitespace with a capture group
# so that we can preserve the whitespace in the output.
if not isinstance(content, str):
msg = "Expected content to be a string."
raise ValueError(msg)View on GitHub (pinned to e32fa9a52e)
Solutions
- Return `ChatResult(generations=[ChatGeneration(message=AIMessage(content=...))])` from `_generate`.
- Or override `_stream` directly instead of `_generate` if you want chunk-level control.
- Use `FakeListChatModel`/`FakeMessagesListChatModel` as-is rather than subclassing with incompatible shapes.
- Add a unit test asserting the return type of your override.
Example fix
# before
def _generate(self, messages, **kw):
return AIMessage(content="hi")
# after
from langchain_core.outputs import ChatResult, ChatGeneration
def _generate(self, messages, **kw):
return ChatResult(generations=[ChatGeneration(message=AIMessage(content="hi"))]) Defensive patterns
Strategy: type-guard
Validate before calling
from langchain_core.outputs import ChatResult
result = fake._generate(messages)
if not isinstance(result, ChatResult):
raise TypeError(f"_generate must return ChatResult, got {type(result)}") Type guard
from langchain_core.outputs import ChatResult
def returns_chat_result(value: object) -> bool:
return isinstance(value, ChatResult) Try / catch
try:
for chunk in fake.stream(messages):
process(chunk)
except ValueError as e:
if "Expected generate to return a ChatResult" in str(e):
raise TypeError("fix fake model _generate return type") from e
raise Prevention
- Return `ChatResult(generations=[ChatGeneration(...)])` from all `_generate` overrides.
- Add type assertions in test fixtures for fakes.
- Prefer stock fakes (`FakeListChatModel`) over hand-rolled ones.
When it happens
Trigger: Subclassing a fake chat model (`FakeChatModel`, used for testing) and overriding `_generate` to return an `AIMessage`, a `str`, a `ChatResult`-like dict, or `None`, then calling `stream`/`invoke` with streaming.
Common situations: Test doubles that shortcut `_generate` to return a plain message; refactors of test fakes after upgrading langchain-core; copying examples that predate the `ChatResult` contract.
Related errors
- Expected invoke to return an AIMessage, but got {type(messag
- Expected content to be a string.
- Unexpected generation type
- messages list cannot be empty.
- AsyncTextProjection received a non-string final value
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/4165a703d1c3fa34.
Report an issue: GitHub.