deepset-ai/haystack · error
Each ChatMessage response must have the 'assistant' role, go
Error message
Each ChatMessage response must have the 'assistant' role, got '{item.role.value}'. What it means
Canned ChatMessage responses for MockChatGenerator must have the assistant role, since the mock simulates the model's reply. Passing a ChatMessage with another role (user, system, tool) raises ValueError stating the actual role found.
Source
Thrown at haystack/components/generators/chat/mock.py:164
items: list[str | ChatMessage]
if isinstance(responses, (str, ChatMessage)):
items = [responses]
elif isinstance(responses, Sequence):
items = list(responses)
else:
raise TypeError(f"'responses' must be a string, ChatMessage, or a sequence of them, got {type(responses)}.")
if len(items) == 0:
raise ValueError("'responses' must not be an empty list.")
normalized: list[ChatMessage] = []
for item in items:
if isinstance(item, str):
normalized.append(ChatMessage.from_assistant(item))
elif isinstance(item, ChatMessage):
if item.role != ChatRole.ASSISTANT:
raise ValueError(
f"Each ChatMessage response must have the 'assistant' role, got '{item.role.value}'."
)
normalized.append(item)
else:
raise TypeError(f"Each response must be a string or ChatMessage, got {type(item)}.")
return normalized
def to_dict(self) -> dict[str, Any]:
"""Serialize the component to a dictionary."""
responses = [msg.to_dict() for msg in self._responses] if self._responses is not None else None
response_fn = serialize_callable(self.response_fn) if self.response_fn is not None else None
streaming_callback = serialize_callable(self.streaming_callback) if self.streaming_callback else None
return default_to_dict(
self,
responses=responses,
response_fn=response_fn,
model=self.model,
meta=self.meta,View on GitHub (pinned to e318778c9b)
Solutions
- Convert the message: ChatMessage.from_assistant(item.text) keeping the content you need
- Filter the list to assistant-role messages before constructing the mock
- Use strings in `responses` (they're auto-wrapped as assistant messages)
Example fix
// before
mock = MockChatGenerator(responses=[ChatMessage.from_user("hello")])
// after
mock = MockChatGenerator(responses=[ChatMessage.from_assistant("hello")]) Defensive patterns
Strategy: validation
Validate before calling
responses = [
m if isinstance(m, str) or m.role == ChatRole.ASSISTANT
else ChatMessage.from_assistant(m.text)
for m in responses
] Type guard
def is_assistant_message(m: ChatMessage) -> bool:
return m.role == ChatRole.ASSISTANT Prevention
- Use plain strings for canned replies (auto-assistant)
- When replaying transcripts, filter to assistant messages only
- Assert roles in fixture builders
When it happens
Trigger: `MockChatGenerator(responses=[ChatMessage.from_user("hi")])` or from_system/from_tool messages in the responses list — commonly when replaying a recorded conversation where user/system messages were included.
Common situations: Replaying captured transcripts without filtering roles; constructing messages with the wrong factory; copying ChatMessage objects from input logs into responses.
Related errors
- Pass either 'responses' or 'response_fn', not both.
- 'responses' must be a string, ChatMessage, or a sequence of
- 'responses' must not be an empty list.
- Each response must be a string or ChatMessage, got {type(ite
- 'response_fn' must return an assistant ChatMessage, got '{re
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/5057e9025b3e0b13.
Report an issue: GitHub.