{"record":{"id":"ec439b55fc1aeb52","repo":"deepset-ai/haystack","slug":"stateschema-handler-for-key-param-must-be-c","errorCode":null,"errorMessage":"StateSchema: 'handler' for key '{param}' must be callable or None","messagePattern":"StateSchema: 'handler' for key '(.+?)' must be callable or None","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/components/agents/state/state.py","lineNumber":73,"sourceCode":"\n\ndef _validate_schema(schema: dict[str, Any]) -> None:\n    \"\"\"\n    Validate that a schema dictionary meets all required constraints.\n\n    Checks that each parameter definition has a valid type field and that any handler\n    specified is a callable function.\n\n    :param schema: Dictionary mapping parameter names to their type and handler configs\n    :raises ValueError: If schema validation fails due to missing or invalid fields\n    \"\"\"\n    for param, definition in schema.items():\n        if \"type\" not in definition:\n            raise ValueError(f\"StateSchema: Key '{param}' is missing a 'type' entry.\")\n        if not _is_valid_type(definition[\"type\"]):\n            raise ValueError(f\"StateSchema: 'type' for key '{param}' must be a Python type, got {definition['type']}\")\n        if definition.get(\"handler\") is not None and not callable(definition[\"handler\"]):\n            raise ValueError(f\"StateSchema: 'handler' for key '{param}' must be callable or None\")\n        if param == \"messages\":  # definition[\"type\"] != list[ChatMessage] but split to cover also List[ChatMessage]\n            if not _is_list_type(definition[\"type\"]):\n                raise ValueError(f\"StateSchema: 'messages' must be of type list[ChatMessage], got {definition['type']}\")\n            # Check if the list contains ChatMessage elements\n            args = get_args(definition[\"type\"])\n            if not args or not issubclass(args[0], ChatMessage):\n                raise ValueError(f\"StateSchema: 'messages' must be of type list[ChatMessage], got {definition['type']}\")\n\n\nclass State:\n    \"\"\"\n    State is a container for storing shared information during the execution of an Agent and its tools.\n\n    For instance, State can be used to store documents, context, and intermediate results.\n\n    Internally it wraps a `_data` dictionary defined by a `schema`. Each schema entry has:\n    ```json\n      \"parameter_name\": {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/agents/state/state.py#L55-L91","documentation":"StateSchema validation raises this ValueError when a schema entry's 'handler' is neither None nor a callable. The handler is invoked on every state.set() to merge old and new values, so it must be a two-argument function.","triggerScenarios":"Passing {\"param\": {\"type\": list, \"handler\": \"merge\"}} (string name instead of the function object) or any non-callable like a dict or result of calling the handler.","commonSituations":"Serializing handlers to strings in configs, accidentally invoking the handler at definition time (handler=merge() instead of handler=merge), or imports resolving to None.","solutions":["Pass the function object itself, not its name or call result: handler=merge_lists","Verify with callable(handler) before building the schema","Set handler to None explicitly if default merge behavior is desired"],"exampleFix":"// before\nschema = {\"numbers\": {\"type\": list, \"handler\": \"merge_numbers\"}}\n// after\nschema = {\"numbers\": {\"type\": list, \"handler\": merge_numbers}}","handlingStrategy":"validation","validationCode":"def handlers_callable(schema):\n    return all(d.get(\"handler\") is None or callable(d[\"handler\"]) for d in schema.values())","typeGuard":"from collections.abc import Callable\ndef is_valid_handler(h) -> bool:\n    return h is None or callable(h)","tryCatchPattern":"try:\n    schema = StateSchema(my_schema)\nexcept ValueError as e:\n    if \"must be callable or None\" in str(e):\n        key = str(e).split(\"'\")[1]\n        my_schema[key][\"handler\"] = HANDLERS[my_schema[key][\"handler\"]]\n    else:\n        raise","preventionTips":["Pass the function object, not its name string","Avoid handler=merge() accidental invocation — no parentheses","Set handler=None explicitly for default merge"],"tags":["python","schema-validation","agent","state"],"backgroundTag":"schema-validation-failed","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}