deepset-ai/haystack · error · ValueError

StateSchema: 'handler' for key '{param}' must be callable or

Error message

StateSchema: 'handler' for key '{param}' must be callable or None

What it means

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.

Source

Thrown at haystack/components/agents/state/state.py:73


def _validate_schema(schema: dict[str, Any]) -> None:
    """
    Validate that a schema dictionary meets all required constraints.

    Checks that each parameter definition has a valid type field and that any handler
    specified is a callable function.

    :param schema: Dictionary mapping parameter names to their type and handler configs
    :raises ValueError: If schema validation fails due to missing or invalid fields
    """
    for param, definition in schema.items():
        if "type" not in definition:
            raise ValueError(f"StateSchema: Key '{param}' is missing a 'type' entry.")
        if not _is_valid_type(definition["type"]):
            raise ValueError(f"StateSchema: 'type' for key '{param}' must be a Python type, got {definition['type']}")
        if definition.get("handler") is not None and not callable(definition["handler"]):
            raise ValueError(f"StateSchema: 'handler' for key '{param}' must be callable or None")
        if param == "messages":  # definition["type"] != list[ChatMessage] but split to cover also List[ChatMessage]
            if not _is_list_type(definition["type"]):
                raise ValueError(f"StateSchema: 'messages' must be of type list[ChatMessage], got {definition['type']}")
            # Check if the list contains ChatMessage elements
            args = get_args(definition["type"])
            if not args or not issubclass(args[0], ChatMessage):
                raise ValueError(f"StateSchema: 'messages' must be of type list[ChatMessage], got {definition['type']}")


class State:
    """
    State is a container for storing shared information during the execution of an Agent and its tools.

    For instance, State can be used to store documents, context, and intermediate results.

    Internally it wraps a `_data` dictionary defined by a `schema`. Each schema entry has:
    ```json
      "parameter_name": {

View on GitHub (pinned to e318778c9b)

Solutions

  1. Pass the function object itself, not its name or call result: handler=merge_lists
  2. Verify with callable(handler) before building the schema
  3. Set handler to None explicitly if default merge behavior is desired

Example fix

// before
schema = {"numbers": {"type": list, "handler": "merge_numbers"}}
// after
schema = {"numbers": {"type": list, "handler": merge_numbers}}
Defensive patterns

Strategy: validation

Validate before calling

def handlers_callable(schema):
    return all(d.get("handler") is None or callable(d["handler"]) for d in schema.values())

Type guard

from collections.abc import Callable
def is_valid_handler(h) -> bool:
    return h is None or callable(h)

Try / catch

try:
    schema = StateSchema(my_schema)
except ValueError as e:
    if "must be callable or None" in str(e):
        key = str(e).split("'")[1]
        my_schema[key]["handler"] = HANDLERS[my_schema[key]["handler"]]
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: Serializing handlers to strings in configs, accidentally invoking the handler at definition time (handler=merge() instead of handler=merge), or imports resolving to None.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/ec439b55fc1aeb52. Report an issue: GitHub.