{"record":{"id":"70e89e76b73392b6","repo":"langchain-ai/langchain","slug":"expected-invoke-to-return-an-aimessage-but-got-t","errorCode":null,"errorMessage":"Expected invoke to return an AIMessage, but got {type(message)} instead.","messagePattern":"Expected invoke to return an AIMessage, but got (.+?) instead\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/language_models/fake_chat_models.py","lineNumber":290,"sourceCode":"    ) -> Iterator[ChatGenerationChunk]:\n        chat_result = self._generate(\n            messages, stop=stop, run_manager=run_manager, **kwargs\n        )\n        if not isinstance(chat_result, ChatResult):\n            msg = (  # type: ignore[unreachable]\n                f\"Expected generate to return a ChatResult, \"\n                f\"but got {type(chat_result)} instead.\"\n            )\n            raise ValueError(msg)  # noqa: TRY004\n\n        message = chat_result.generations[0].message\n\n        if not isinstance(message, AIMessage):\n            msg = (\n                f\"Expected invoke to return an AIMessage, \"\n                f\"but got {type(message)} instead.\"\n            )\n            raise ValueError(msg)  # noqa: TRY004\n\n        content = message.content\n\n        if content:\n            # Use a regular expression to split on whitespace with a capture group\n            # so that we can preserve the whitespace in the output.\n            if not isinstance(content, str):\n                msg = \"Expected content to be a string.\"\n                raise ValueError(msg)\n\n            content_chunks = cast(\"list[str]\", re.split(r\"(\\s)\", content))\n\n            for idx, token in enumerate(content_chunks):\n                chunk = ChatGenerationChunk(\n                    message=AIMessageChunk(content=token, id=message.id)\n                )\n                if (\n                    idx == len(content_chunks) - 1","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/language_models/fake_chat_models.py#L272-L308","documentation":"`ValueError` in the fake chat model streaming bridge: `chat_result.generations[0].message` is not an `AIMessage`. The streaming code chunks string content off an assistant message; other message types (e.g. a parrot-style echo of a `HumanMessage`) violate that expectation.","triggerScenarios":"A fake/custom `_generate` that returns a `ChatResult` whose first generation holds a `HumanMessage`, `SystemMessage`, or `ToolMessage` instead of an `AIMessage`, followed by a streaming call.","commonSituations":"Echo/parrot test fakes that return `messages[-1]` unfiltered (when the last input message is human); building canned responses with the wrong message class; test fixtures recorded from prompts rather than responses.","solutions":["Wrap the response in `AIMessage(content=...)` inside `_generate`.","If echoing input, coerce: `AIMessage(content=messages[-1].content)`.","Assert message types in test fixtures before wiring them into the fake."],"exampleFix":"# before\nreturn ChatResult(generations=[ChatGeneration(message=messages[-1])])  # may be HumanMessage\n\n# after\nfrom langchain_core.messages import AIMessage\nreturn ChatResult(generations=[ChatGeneration(message=AIMessage(content=messages[-1].content))])","handlingStrategy":"type-guard","validationCode":"from langchain_core.messages import AIMessage\nmsg = fake._generate(messages).generations[0].message\nif not isinstance(msg, AIMessage):\n    raise TypeError(f\"fake must produce AIMessage, got {type(msg)}\")","typeGuard":"from langchain_core.messages import AIMessage\ndef is_ai_message(m: object) -> bool:\n    return isinstance(m, AIMessage)","tryCatchPattern":"try:\n    list(fake.stream(messages))\nexcept ValueError as e:\n    if \"Expected invoke to return an AIMessage\" in str(e):\n        raise TypeError(\"wrap fake output in AIMessage(content=...)\") from e\n    raise","preventionTips":["Echo fakes should coerce input content into `AIMessage(content=...)`.","Never return `messages[-1]` raw when it can be a `HumanMessage`.","Type-annotate fake `_generate` return values in tests."],"tags":["fake-model","testing","messages","type-mismatch"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}