{"record":{"id":"fcd9536172a0a6f6","repo":"langchain-ai/langchain","slug":"expected-content-to-be-a-string","errorCode":null,"errorMessage":"Expected content to be a string.","messagePattern":"Expected content to be a string\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/language_models/fake_chat_models.py","lineNumber":299,"sourceCode":"            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\n                    and isinstance(chunk.message, AIMessageChunk)\n                    and not message.additional_kwargs\n                ):\n                    chunk.message.chunk_position = \"last\"\n                if run_manager:\n                    run_manager.on_llm_new_token(token, chunk=chunk)\n                yield chunk\n\n        if message.additional_kwargs:","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/language_models/fake_chat_models.py#L281-L317","documentation":"`ValueError` in the fake chat model streaming bridge: the produced `AIMessage.content` is non-empty but not a `str` (e.g. a list of content blocks). The tokenizer split `re.split(r\"(\\s)\", content)` requires plain string content.","triggerScenarios":"A fake `_generate` returning `AIMessage(content=[{\"type\": \"text\", \"text\": \"hi\"}])` or other multimodal list content, then streaming from the fake.","commonSituations":"Recording real multimodal provider responses into test fixtures and replaying them through a fake; constructing fakes from Anthropic/OpenAI content-block formats.","solutions":["Flatten list content to a string in the fake: `content=\"hi\"` instead of block lists.","Extract text from blocks first: `\"\".join(b[\"text\"] for b in content if b.get(\"type\") == \"text\")`.","Or bypass the fake streaming bridge by overriding `_stream` directly for multimodal fixtures."],"exampleFix":"# before\nreturn ChatResult(generations=[ChatGeneration(message=AIMessage(content=[{\"type\":\"text\",\"text\":\"hi\"}]))])\n\n# after\nreturn ChatResult(generations=[ChatGeneration(message=AIMessage(content=\"hi\"))])","handlingStrategy":"type-guard","validationCode":"content = fake._generate(messages).generations[0].message.content\nif content and not isinstance(content, str):\n    content = \"\".join(b.get(\"text\", \"\") for b in content if isinstance(b, dict))","typeGuard":"def is_string_content(content: object) -> bool:\n    return not content or isinstance(content, str)","tryCatchPattern":"try:\n    list(fake.stream(messages))\nexcept ValueError as e:\n    if \"Expected content to be a string\" in str(e):\n        raise TypeError(\"flatten multimodal content in the fake before streaming\") from e\n    raise","preventionTips":["Keep fake responses as plain strings.","Flatten recorded multimodal content blocks before using them in fakes.","Override `_stream` directly when testing multimodal output shapes."],"tags":["fake-model","testing","multimodal","type-mismatch"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}