deepset-ai/haystack · error · ValueError
A `ChatMessage` must contain at least one `TextContent`, `To
Error message
A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, `ToolCallResult`,
`ImageContent`, `FileContent`, `ReasoningContent`. What it means
A ChatMessage with an empty content list cannot be serialized for the OpenAI Responses API; haystack raises ValueError because the API requires at least one content item (text, tool call, tool result, image, file, or reasoning).
Source
Thrown at haystack/components/generators/chat/openai_responses.py:996
if isinstance(part, FileContent):
return {
"type": "input_file",
# Filename is optional but if not provided, OpenAI expects a file_id of a previous file upload.
# We use a dummy filename to avoid this issue.
"filename": part.filename or "filename",
"file_data": f"data:{part.mime_type or 'application/pdf'};base64,{part.base64_data}",
}
raise ValueError(f"Unsupported content type: {type(part)}")
text_contents = message.texts
tool_calls = message.tool_calls
tool_call_results = message.tool_call_results
images = message.images
reasonings = message.reasonings
files = message.files
if not any([text_contents, tool_calls, tool_call_results, images, reasonings, files]):
raise ValueError(
"""A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, `ToolCallResult`,
`ImageContent`, `FileContent`, or `ReasoningContent`."""
)
if len(tool_call_results) > 0 and len(message._content) > 1:
raise ValueError(
"For OpenAI compatibility, a `ChatMessage` with a `ToolCallResult` cannot contain any other content."
)
formatted_messages: list[dict[str, Any]] = []
openai_msg: dict[str, Any] = {"role": message._role.value}
if message._name is not None:
openai_msg["name"] = message._name
# user message
if message._role.value == "user":
content = [convert_part(part) for part in message._content]
openai_msg["content"] = content
return [openai_msg]View on GitHub (pinned to e318778c9b)
Solutions
- Ensure every message passed to the generator has at least one content part
- Skip or drop empty messages from the message list before run()
- Add a placeholder TextContent if the message must be sent
Example fix
// before
msg = ChatMessage(_role=ChatRole.USER, _content=[])
// after
msg = ChatMessage.from_user("(empty)") if not contents else ChatMessage(_role=ChatRole.USER, _content=contents) Defensive patterns
Strategy: validation
Validate before calling
for m in messages:
if not m._content:
raise ValueError("message has no content") # or drop it Type guard
def has_content(m) -> bool:
return bool(m._content) Try / catch
try:
gen.run(messages=messages)
except ValueError as e:
if "must contain at least one" in str(e):
messages = [m for m in messages if m._content]
gen.run(messages=messages) Prevention
- Filter empty messages before run()
- Never construct ChatMessage with _content=[]
- Assert message._content after dynamic building
When it happens
Trigger: Calling OpenAIResponsesChatGenerator.run/count with a ChatMessage constructed with no contents, e.g. ChatMessage(_role=ChatRole.USER, _content=[]).
Common situations: Building messages dynamically where all branches skipped adding content; filtering out contents (e.g. stripping images) leaving an empty message.
Related errors
- Cannot stream multiple responses, please set n=1.
- 'response_fn' must return an assistant ChatMessage, got '{re
- Unsupported content type: {type(part)}
- For OpenAI compatibility, a `ChatMessage` with a `ToolCallRe
- Unsupported tool result: {result.result}
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/bc96298c5faf38f7.
Report an issue: GitHub.