deepset-ai/haystack · error · SchemaGenerationError

Failed to create JSON schema for the run method of Component

Error message

Failed to create JSON schema for the run method of Component '{component.__class__.__name__}'

What it means

Raised by ComponentTool._create_tool_parameters_schema when Pydantic fails to build a JSON schema from the component's run() method signature. ComponentTool wraps a Haystack Component so it can be exposed as a Tool; to do that it introspects run()'s parameters with type hints and calls create_model(...).model_json_schema(). If any parameter annotation cannot be converted to a JSON schema (unsupported types, broken generics, forward references that don't resolve), the underlying exception is chained and re-raised as SchemaGenerationError.

Source

Thrown at haystack/tools/component_tool.py:368

            if _unwrap_optional(input_type) is State:
                continue

            description = param_descriptions.get(input_name, f"Input '{input_name}' for the component.")

            # if the parameter has not a default value, Pydantic requires an Ellipsis (...)
            # to explicitly indicate that the parameter is required
            default = ... if socket.is_mandatory else socket.default_value
            resolved_type = _resolve_type(input_type)
            fields[input_name] = (resolved_type, Field(default=default, description=description))

        parameters_schema: dict[str, Any] = {}
        try:
            # No `__doc__`: it would surface as a top-level `description` on the parameters schema,
            # which LLM providers ignore. The component description feeds the tool-level description.
            model = create_model(component.run.__name__, **fields)
            parameters_schema = model.model_json_schema()
        except Exception as e:
            raise SchemaGenerationError(
                f"Failed to create JSON schema for the run method of Component '{component.__class__.__name__}'"
            ) from e

        # we don't want to include title keywords in the schema, as they contain redundant information
        # there is no programmatic way to prevent Pydantic from adding them, so we remove them later
        # see https://github.com/pydantic/pydantic/discussions/8504
        _remove_title_from_schema(parameters_schema)

        return parameters_schema

    def _convert_param(self, param_value: Any, param_type: type) -> Any:
        """
        Converts a single parameter value to the expected type.

        :param param_value: The value to convert.
        :param param_type: The expected type of the parameter.

        :returns:

View on GitHub (pinned to e318778c9b)

Solutions

  1. Inspect the chained exception (__cause__) to find which run() parameter failed schema generation
  2. Change the offending run() parameter type hint to a JSON-serializable Pydantic-compatible type (str, int, list, dict, BaseModel)
  3. Remove or replace unsupported annotations like plain Callable, custom non-serializable classes, or unresolved forward references
  4. Provide a static 'parameters' schema via Tool instead of letting ComponentTool derive it from run()

Example fix

// before
class MyComponent:
    def run(self, client: SomeClient) -> dict:
        ...

// after
class MyComponent:
    def run(self, endpoint: str) -> dict:
        ...
Defensive patterns

Strategy: validation

Validate before calling

import inspect
from typing import get_type_hints

def validate_component_run_schema(component) -> str | None:
    try:
        hints = get_type_hints(component.run)
    except Exception as e:
        return f"Unresolvable type hints on run(): {e}"
    for name, hint in hints.items():
        if name == "return":
            continue
        if not hasattr(hint, "__origin__") and hint in (object, type(None)):
            return f"Parameter '{name}' has non-serializable hint {hint}"
    return None

problem = validate_component_run_schema(my_component)
assert problem is None, problem

Type guard

def has_annotated_run(component) -> bool:
    params = inspect.signature(component.run).parameters
    return all(p.annotation is not inspect.Parameter.empty for p in params.values())

Try / catch

from haystack.tools.errors import SchemaGenerationError

try:
    tool = ComponentTool(component=my_component)
except SchemaGenerationError as e:
    logger.error("run() signature not schema-able: %s", e.__cause__)
    raise

Prevention

When it happens

Trigger: ComponentTool(component=SomeComponent()) where the component's run() method has a parameter type hint that Pydantic cannot model, e.g. an unresolvable forward reference, a Callable without parameters, arbitrary classes, or a badly parameterized generic.

Common situations: Writing a custom component whose run() takes non-serializable objects (DB connections, clients, callables); renaming/moving types so string annotations no longer resolve; Python version changes altering typing semantics.

Related errors


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