{"record":{"id":"19aacedf1bbcb65f","repo":"deepset-ai/haystack","slug":"outputs-to-state-configuration-for-key-key-mus","errorCode":null,"errorMessage":"outputs_to_state configuration for key '{key}' must be a dictionary","messagePattern":"outputs_to_state configuration for key '(.+?)' must be a dictionary","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"haystack/tools/tool.py","lineNumber":141,"sourceCode":"\n        # `async_function` must be a coroutine function defined with `async def`.\n        if self.async_function is not None and not inspect.iscoroutinefunction(self.async_function):\n            raise ValueError(\n                f\"`async_function` must be a coroutine function defined with `async def`. \"\n                f\"Got '{getattr(self.async_function, '__name__', repr(self.async_function))}'.\"\n            )\n\n        # Check that the parameters define a valid JSON schema\n        try:\n            Draft202012Validator.check_schema(self.parameters)\n        except SchemaError as e:\n            raise ValueError(\"The provided parameters do not define a valid JSON schema\") from e\n\n        # Validate outputs structure if provided\n        if self.outputs_to_state is not None:\n            for key, config in self.outputs_to_state.items():\n                if not isinstance(config, dict):\n                    raise TypeError(f\"outputs_to_state configuration for key '{key}' must be a dictionary\")\n                if \"source\" in config and not isinstance(config[\"source\"], str):\n                    raise ValueError(f\"outputs_to_state source for key '{key}' must be a string.\")\n                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):","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/tools/tool.py#L123-L159","documentation":"Each value in Tool.outputs_to_state must itself be a dict describing that state key's config (optional \"source\" and \"handler\"). __post_init__ raises TypeError if any per-key configuration is not a dictionary, catching configs passed as strings, callables, or other objects.","triggerScenarios":"Tool(..., outputs_to_state={\"result\": some_handler}) or outputs_to_state={\"result\": \"output_name\"} — passing the handler or source directly instead of wrapping it, e.g. {\"result\": {\"handler\": fn}}.","commonSituations":"Older Haystack API style where outputs_to_state mapped keys directly to callables; copy-pasted config from older examples after the API changed to nested dicts.","solutions":["Wrap each value in a dict: outputs_to_state={\"result\": {\"handler\": my_handler}}.","If mapping a specific output, use {\"result\": {\"source\": \"output_name\", \"handler\": fn}}.","Update code written for older haystack Tool APIs to the nested-dict config format."],"exampleFix":"// before\nTool(name=\"t\", function=f, outputs_to_state={\"answer\": my_handler})\n\n// after\nTool(name=\"t\", function=f, outputs_to_state={\"answer\": {\"handler\": my_handler}})","handlingStrategy":"type-guard","validationCode":"assert all(isinstance(v, dict) for v in outputs_to_state.values()), \"each outputs_to_state value must be a dict\"","typeGuard":"def is_valid_outputs_to_state(o: object) -> bool:\n    return isinstance(o, dict) and all(isinstance(v, dict) for v in o.values())","tryCatchPattern":"try:\n    tool = Tool(name=\"t\", function=f, outputs_to_state=ots)\nexcept TypeError as e:\n    raise ValueError(f\"Bad outputs_to_state config: {e}\") from e","preventionTips":["Always wrap per-key config in a dict, e.g. {\"k\": {\"handler\": fn}}","Update configs written for older haystack Tool APIs that passed callables directly","Add a construction-time unit test per tool definition"],"tags":["python","tool","config","type-error"],"backgroundTag":"invalid-config-shape","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}