{"record":{"id":"3b47667805d8c549","repo":"deepset-ai/haystack","slug":"response-fn-must-return-a-string-or-chatmessage","errorCode":null,"errorMessage":"'response_fn' must return a string or ChatMessage, got {type(result)}.","messagePattern":"'response_fn' must return a string or ChatMessage, got (.+?)\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"haystack/components/generators/chat/mock.py","lineNumber":236,"sourceCode":"        Callables that accept a single positional argument are called as `response_fn(messages)` instead.\n        \"\"\"\n        try:\n            inspect.signature(response_fn).bind(None, None)\n        except (TypeError, ValueError):\n            # The callable rejects a second positional argument, or exposes no signature at all (some C callables).\n            return False\n        return True\n\n    @staticmethod\n    def _coerce_to_message(result: str | ChatMessage) -> ChatMessage:\n        \"\"\"Turn the output of `response_fn` into a `ChatMessage`, wrapping strings and requiring the assistant role.\"\"\"\n        if isinstance(result, str):\n            return ChatMessage.from_assistant(result)\n        if isinstance(result, ChatMessage):\n            if result.role != ChatRole.ASSISTANT:\n                raise ValueError(f\"'response_fn' must return an assistant ChatMessage, got '{result.role.value}'.\")\n            return result\n        raise TypeError(f\"'response_fn' must return a string or ChatMessage, got {type(result)}.\")\n\n    @staticmethod\n    def _estimate_usage(messages: list[ChatMessage], reply: ChatMessage) -> dict[str, int]:\n        \"\"\"\n        Roughly estimate token usage as whitespace-separated word counts.\n\n        This is an approximation (not real tokenization) intended to give downstream code realistic-looking metadata.\n        \"\"\"\n        prompt_tokens = sum(len((message.text or \"\").split()) for message in messages)\n        completion_tokens = len((reply.text or \"\").split())\n        return {\n            \"prompt_tokens\": prompt_tokens,\n            \"completion_tokens\": completion_tokens,\n            \"total_tokens\": prompt_tokens + completion_tokens,\n        }\n\n    def _build_meta(self, messages: list[ChatMessage], base: ChatMessage) -> dict[str, Any]:\n        \"\"\"Build the metadata attached to the returned reply, merging defaults, init meta, and per-response meta.\"\"\"","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/generators/chat/mock.py#L218-L254","documentation":"`_coerce_to_message` only accepts str or ChatMessage from `response_fn`. Any other type (dict, list, None, model output object) raises TypeError, because MockChatGenerator has no way to convert arbitrary values into a ChatMessage.","triggerScenarios":"`response_fn` returns None (forgot a return), a dict like {\"text\": ...}, a list of messages, or an SDK response object instead of str/ChatMessage.","commonSituations":"Lambdas that print or log instead of returning; copying response handling code from other SDKs that return raw API payloads; forgetting `return` in a one-line lambda.","solutions":["Ensure response_fn returns either a str or a ChatMessage","Wrap dict/list results: ChatMessage.from_assistant(str) or ChatMessage().created from the appropriate constructor","Debug with a print/log of type(result) inside response_fn to find what is actually returned"],"exampleFix":"// before\nresponse_fn=lambda msgs: {\"text\": \"hi\"}\n// after\nresponse_fn=lambda msgs: \"hi\"","handlingStrategy":"type-guard","validationCode":"result = response_fn(messages)\nif not isinstance(result, (str, ChatMessage)):\n    raise TypeError(f\"response_fn returned {type(result)}\")","typeGuard":"def is_str_or_chat_message(v) -> bool:\n    return isinstance(v, (str, ChatMessage))","tryCatchPattern":"try:\n    gen.run([msg])\nexcept TypeError as e:\n    if \"response_fn\" in str(e):\n        logging.error(\"response_fn returned %s\", type(result))","preventionTips":["Don't forget return in lambdas","Return str for simple canned replies","Log type(result) when debugging mocks"],"tags":["python","haystack","mock","type-error"],"backgroundTag":"unexpected-return-type","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}