{"record":{"id":"f1c49b69a3e22140","repo":"deepset-ai/haystack","slug":"invalid-outputs-to-string-config-when-using-sour","errorCode":null,"errorMessage":"Invalid outputs_to_string config. When using 'source', 'handler' or 'raw_result' at the root level, no other keys are  allowed. Use individual output configs instead.","messagePattern":"Invalid outputs_to_string config\\. When using 'source', 'handler' or 'raw_result' at the root level, no other keys are  allowed\\. Use individual output configs instead\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/tools/tool.py","lineNumber":174,"sourceCode":"                        )\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\n                for key, config in self.outputs_to_string.items():\n                    if not isinstance(config, dict):\n                        raise TypeError(f\"outputs_to_string configuration for key '{key}' must be a dictionary\")\n                    if \"raw_result\" in config:\n                        raise ValueError(\n                            f\"Invalid outputs_to_string configuration for key '{key}': \"\n                            f\"'raw_result' is not supported in the multiple output format.\"\n                        )\n                    if \"source\" not in config:\n                        raise ValueError(\n                            f\"Invalid outputs_to_string configuration for key '{key}': \"\n                            f\"each output must have a 'source' defined.\"","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/tools/tool.py#L156-L192","documentation":"outputs_to_string supports two shapes: a single-output config whose keys are only source/handler/raw_result, or a per-output config keyed by output names. Mixing them — root-level source/handler/raw_result alongside any other key — is rejected with this ValueError at Tool construction.","triggerScenarios":"Tool(..., outputs_to_string={\"source\": \"answer\", \"documents\": {\"handler\": fn}}) — a root-level key plus an output-name key; or {\"raw_result\": True, \"foo\": {...}}.","commonSituations":"Merging two configs (one single-output, one multi-output) with dict update; incremental edits adding a per-output entry to an existing single-output config.","solutions":["Use one output-name key per entry with no root-level keys: {\"answer\": {\"handler\": fn}} instead of {\"source\": \"answer\", \"handler\": fn} mixed with others.","If one output needs config and others don't, give each output its own (possibly empty) dict.","Split into two configs only if using two separate Tools; a single Tool must use a uniform shape."],"exampleFix":"// before\noutputs_to_string={\"source\": \"answer\", \"handler\": fmt, \"documents\": {\"handler\": doc_fmt}}\n\n// after\noutputs_to_string={\"answer\": {\"handler\": fmt}, \"documents\": {\"handler\": doc_fmt}}","handlingStrategy":"validation","validationCode":"ots = outputs_to_string or {}\nROOT_KEYS = {\"source\", \"handler\", \"raw_result\"}\nif (ROOT_KEYS & ots.keys()) and (ots.keys() - ROOT_KEYS):\n    raise ValueError(\"outputs_to_string mixes root-level keys with per-output configs\")","typeGuard":"def is_uniform_outputs_to_string(ots: dict | None) -> bool:\n    if ots is None:\n        return True\n    ROOT = {\"source\", \"handler\", \"raw_result\"}\n    return not (ROOT & ots.keys()) or not (ots.keys() - ROOT)","tryCatchPattern":"try:\n    tool = Tool(name=\"t\", function=f, outputs_to_string=ots)\nexcept ValueError as e:\n    if \"Invalid outputs_to_string\" in str(e):\n        ots = {k: (v if isinstance(v, dict) else {k2: v for k2 in (\"source\", \"handler\", \"raw_result\") if k2 == k} or {}) for k, v in [(\"answer\", ots)]}\n    raise","preventionTips":["Pick one shape: per-output keys only, or root-level keys only","Don't merge single-output and multi-output configs with dict update","Validate the mixed-key condition in tests before constructing Tools"],"tags":["python","tool","config","validation"],"backgroundTag":"invalid-config-shape","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}