{"record":{"id":"e4125378d27b8fee","repo":"deepset-ai/haystack","slug":"outputs-to-string-handler-must-be-callable","errorCode":null,"errorMessage":"outputs_to_string handler must be callable","messagePattern":"outputs_to_string handler must be callable","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/tools/tool.py","lineNumber":162,"sourceCode":"                if \"handler\" in config and not callable(config[\"handler\"]):\n                    raise ValueError(f\"outputs_to_state handler for key '{key}' must be callable\")\n\n            # Validate that outputs_to_state source keys exist as valid tool outputs\n            valid_outputs: set[str] | None = self._get_valid_outputs()\n            if valid_outputs is not None:\n                for state_key, config in self.outputs_to_state.items():\n                    source = config.get(\"source\")\n                    if source is not None and source not in valid_outputs:\n                        raise ValueError(\n                            f\"outputs_to_state: '{self.name}' maps state key '{state_key}' to unknown output '{source}'\"\n                            f\"Valid outputs are: {valid_outputs}.\"\n                        )\n\n        if self.outputs_to_string is not None:\n            if \"source\" in self.outputs_to_string and not isinstance(self.outputs_to_string[\"source\"], str):\n                raise ValueError(\"outputs_to_string source must be a string.\")\n            if \"handler\" in self.outputs_to_string and not callable(self.outputs_to_string[\"handler\"]):\n                raise ValueError(\"outputs_to_string handler must be callable\")\n            if \"raw_result\" in self.outputs_to_string and not isinstance(self.outputs_to_string[\"raw_result\"], bool):\n                raise ValueError(\"outputs_to_string raw_result must be a boolean.\")\n\n            if (\n                \"source\" in self.outputs_to_string\n                or \"handler\" in self.outputs_to_string\n                or \"raw_result\" in self.outputs_to_string\n            ):\n                # Single output configuration\n                for key in self.outputs_to_string:\n                    if key not in {\"source\", \"handler\", \"raw_result\"}:\n                        raise ValueError(\n                            \"Invalid outputs_to_string config. \"\n                            \"When using 'source', 'handler' or 'raw_result' at the root level, no other keys are \"\n                            \" allowed. Use individual output configs instead.\"\n                        )\n            else:\n                # Multiple outputs configuration","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/tools/tool.py#L144-L180","documentation":"A root-level \"handler\" in outputs_to_string must be a callable that converts the tool output to a string. __post_init__ raises ValueError if \"handler\" is present but not callable.","triggerScenarios":"Tool(..., outputs_to_string={\"handler\": \"str\"}) is fine (str is callable) but {\"handler\": None}, {\"handler\": \"my_converter\"} (a name string) raise the error.","commonSituations":"Passing the handler's name/qualname instead of the function object in config-driven setups; forgetting the import so a variable is a string.","solutions":["Pass the callable itself: {\"handler\": my_converter}.","Resolve string handler names to callables via a registry before Tool construction.","Remove the key if only the default str conversion is wanted."],"exampleFix":"// before\noutputs_to_string={\"handler\": \"json_dumps\"}\n\n// after\nimport json\noutputs_to_string={\"handler\": json.dumps}","handlingStrategy":"type-guard","validationCode":"ots = outputs_to_string or {}\nif \"handler\" in ots and not callable(ots[\"handler\"]):\n    raise TypeError(\"outputs_to_string handler must be callable\")","typeGuard":"def valid_root_handler(ots: dict | None) -> bool:\n    import builtins\n    return ots is None or callable(ots.get(\"handler\", builtins.str))","tryCatchPattern":"try:\n    tool = Tool(name=\"t\", function=f, outputs_to_string=ots)\nexcept ValueError as e:\n    if \"handler must be callable\" in str(e):\n        logger.error(f\"Resolve outputs_to_string handler: {e}\")\n    raise","preventionTips":["Pass function objects, not names","Resolve string handler names via a registry before Tool construction","Unit-test each serializer with the actual tool output shape"],"tags":["python","tool","config","callable"],"backgroundTag":"handler-not-callable","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}