deepset-ai/haystack · error · TypeError
Each response must be a string or ChatMessage, got {type(ite
Error message
Each response must be a string or ChatMessage, got {type(item)}. What it means
Inside a `responses` sequence, each element must be a string or a ChatMessage. Any other item type (dict, int, None, bytes) raises TypeError naming the item's type, raised from _normalize_responses during __init__.
Source
Thrown at haystack/components/generators/chat/mock.py:169
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,
streaming_callback=streaming_callback,
)
@classmethod
def from_dict(cls, data: dict[str, Any]) -> MockChatGenerator:View on GitHub (pinned to e318778c9b)
Solutions
- Convert dicts with ChatMessage.from_dict(d) before passing
- Use plain strings for simple text replies
- Sanitize/mixed lists: map each item through str/ChatMessage.from_dict as appropriate
Example fix
// before
mock = MockChatGenerator(responses=[{"text": "hi", "role": "assistant"}])
// after
mock = MockChatGenerator(responses=[ChatMessage.from_dict({"text": "hi", "role": "assistant"})]) Defensive patterns
Strategy: type-guard
Validate before calling
def coerce_responses(items) -> list[ChatMessage]:
out = []
for i in items:
if isinstance(i, str):
out.append(ChatMessage.from_assistant(i))
elif isinstance(i, ChatMessage):
out.append(i)
elif isinstance(i, dict):
out.append(ChatMessage.from_dict(i))
else:
raise TypeError(f"bad response item: {type(i)}")
return out Type guard
def is_response_item(i) -> bool:
return isinstance(i, (str, ChatMessage)) Prevention
- Deserialize stored responses with ChatMessage.from_dict before passing
- Keep fixture lists homogeneous: all strings or all ChatMessages
- Annotate response builders with list[str | ChatMessage] return types
When it happens
Trigger: `MockChatGenerator(responses=[{"text": "hi"}])` or [None], or a list mixing strings with dicts/ints — often from JSON-loaded data where ChatMessages deserialize as dicts.
Common situations: Loading canned responses from JSON/YAML fixtures and forgetting to call ChatMessage.from_dict; building mixed lists programmatically; passing raw message dicts from another framework.
Related errors
- 'responses' must be a string, ChatMessage, or a sequence of
- Each ChatMessage response must have the 'assistant' role, go
- MockDocumentEmbedder expects a list of Documents as input.In
- MockTextEmbedder expects a string as an input. In case you w
- {name} must be a sequence of numbers, got {type(value)}.
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/efa78e9a3cc3f8e7.
Report an issue: GitHub.