deepset-ai/haystack · error · TypeError

Expected ChatMessage object, got {type(last_message)}

Error message

Expected ChatMessage object, got {type(last_message)}

What it means

RegexTextExtractor processes the input list and expects its last element to be a ChatMessage. If the last element is any other type (e.g. a plain string or Document), _process_last_message raises TypeError naming the received type.

Source

Thrown at haystack/components/extractors/regex_text_extractor.py:122

            logger.warning("Received empty list of messages")
            return {"captured_text": ""}
        return self._process_last_message(text_or_messages)

    def _build_result(self, result: str | list[str]) -> dict:
        """Helper method to build the return dictionary based on configuration."""
        if (isinstance(result, str) and result == "") or (isinstance(result, list) and not result):
            return {"captured_text": ""}
        return {"captured_text": result}

    def _process_last_message(self, messages: list[ChatMessage]) -> dict:
        """
        Process only the last message and build the result.

        :raises TypeError: If the last element of the list is not a ChatMessage instance.
        """
        last_message = messages[-1]
        if not isinstance(last_message, ChatMessage):
            raise TypeError(f"Expected ChatMessage object, got {type(last_message)}")
        if last_message.text is None:
            logger.warning("Last message has no text content")
            return {"captured_text": ""}
        result = self._extract_from_text(last_message.text)
        return self._build_result(result)

    def _extract_from_text(self, text: str) -> str | list[str]:
        """
        Extract text using the regex pattern.

        :param text:
            The text to search through.

        :returns:
            The text captured by the first capturing group in the regex pattern.
            If the pattern has no capture groups, returns the entire match.
            If no match is found, returns an empty string.
        """

View on GitHub (pinned to e318778c9b)

Solutions

  1. Ensure the input is a list whose last element is a ChatMessage, e.g. run(messages=[ChatMessage.from_user(text)])
  2. Convert strings before passing: ChatMessage.from_user(my_string)
  3. Check the upstream component's output type and add an adapter component if it emits non-ChatMessage values

Example fix

// before
extractor.run(messages=["some plain text"])
// after
from haystack.dataclasses import ChatMessage
extractor.run(messages=[ChatMessage.from_user("some plain text")])
Defensive patterns

Strategy: type-guard

Validate before calling

if not messages or not isinstance(messages[-1], ChatMessage):
    raise TypeError("Last message must be a ChatMessage")

Type guard

from haystack.dataclasses import ChatMessage

def is_chat_message_list(messages: list) -> bool:
    return bool(messages) and isinstance(messages[-1], ChatMessage)

Try / catch

try:
    result = extractor.run(messages=messages)
except TypeError as e:
    logger.error("RegexTextExtractor input error: %s", e)
    raise

Prevention

When it happens

Trigger: Calling run(messages=[...]) where messages[-1] is not a ChatMessage instance — e.g. feeding raw strings from a custom component, or an upstream component emitting Documents instead of chat messages.

Common situations: Connecting a non-chat component's output to the extractor; manually constructing test inputs as strings; migration from string-based to chat-message-based pipelines.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/54a88797552a4c42. Report an issue: GitHub.