deepset-ai / deepset-ai/haystack
ChatMessage.from_openai_dict_format wraps list-of-parts content in TextContent, breaking msg.text contract
@julian-risch is already working on this.
Since Aug 24, 2026.
- Dominant language
- Python
- Stars
- 26.6k
- Forks
- 3.2k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 194
Description
Describe the bug
ChatMessage.from_openai_dict_format only handles content as a plain string for user, system, and developer messages. When content is a list of content parts (the standard OpenAI Chat Completions format for multimodal messages, and also valid for text-only messages), the whole list is passed verbatim into a single TextContent. Only the tool role branch handles list content.
As a result:
msg.textreturns alistinstead ofstr, violating thestr | Nonecontract ofChatMessage.text/TextContent.text.msg.to_dict()andmsg.to_openai_dict_format()emit invalid content (a list nested where a string is expected), so the message cannot be round-tripped or sent back to OpenAI.- Image/file parts are silently corrupted into "text" instead of
ImageContent/FileContent.
To Reproduce
from haystack.dataclasses import ChatMessage
b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="
msg = ChatMessage.from_openai_dict_format({
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
],
})
print(type(msg.text)) # <class 'list'> — should be str
print(msg.images) # [] — the image part is lost
print(msg.to_openai_dict_format())
# {'role': 'user', 'content': [{'type': 'text', 'text': 'What is in this image?'}, ...]}
# is what you'd expect, but instead 'content' is the raw list stringified inside a TextContent,
# producing an invalid OpenAI payload on re-serialization
Expected behavior
{"type": "text"}parts becomeTextContent,{"type": "image_url"}parts with base64 data URLs becomeImageContent,{"type": "file"}parts with inlinefile_databecomeFileContent(the exact inverse ofto_openai_dict_format, which already produces these part formats).msg.textreturns the first text part asstr.system/developermessages accept lists of text parts.- Unsupported parts (non-data image URLs,
file_idreferences, unknown types) raiseValueErrorinstead of silently corrupting the message.
Describe your environment (please complete the following information):
- OS: macOS
- Haystack version: current
main(d10abda, 3.2.0rc0); also reproducible on 2.x releases - Python version: 3.13
I have a fix ready and will open a PR shortly.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.