{"record":{"id":"a54e6fa6b1af465f","repo":"deepset-ai/haystack","slug":"function-function-name-parameter-param","errorCode":null,"errorMessage":"Function '{function.__name__}': parameter '{param_name}' does not have a type hint.","messagePattern":"Function '(.+?)': parameter '(.+?)' does not have a type hint\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/tools/from_function.py","lineNumber":151,"sourceCode":"    tool_description = description if description is not None else (function.__doc__ or \"\")\n\n    signature = inspect.signature(function)\n\n    # collect fields (types and defaults) and descriptions from function parameters\n    fields: dict[str, Any] = {}\n    descriptions = {}\n\n    for param_name, param in signature.parameters.items():\n        # Skip adding parameter names that will be passed to the tool from State\n        if inputs_from_state and param_name in inputs_from_state.values():\n            continue\n\n        # Skip State-typed parameters (including Optional[State]) - Agent tool execution injects them at runtime\n        if _unwrap_optional(param.annotation) is State:\n            continue\n\n        if param.annotation is param.empty:\n            raise ValueError(f\"Function '{function.__name__}': parameter '{param_name}' does not have a type hint.\")\n\n        # Skip Callable types since Pydantic cannot generate JSON schemas for them\n        if _contains_callable_type(param.annotation):\n            continue\n\n        # if the parameter has not a default value, Pydantic requires an Ellipsis (...)\n        # to explicitly indicate that the parameter is required\n        default = param.default if param.default is not param.empty else ...\n        fields[param_name] = (param.annotation, default)\n\n        if hasattr(param.annotation, \"__metadata__\"):\n            descriptions[param_name] = param.annotation.__metadata__[0]\n\n    # create Pydantic model and generate JSON schema\n    try:\n        model = create_model(function.__name__, **fields)\n        schema = model.model_json_schema()\n    except Exception as e:","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/tools/from_function.py#L133-L169","documentation":"create_tool_from_function builds a tool schema from a plain function's signature; every parameter (except State-typed ones and Callables) must carry a type annotation, because a Pydantic model cannot be generated without one. If any parameter's annotation is empty (param.empty), a ValueError is raised naming the function and parameter.","triggerScenarios":"@tool or create_tool_from_function(fn) where fn has an un-annotated parameter, e.g. def search(query, limit=5): ...","commonSituations":"Quick scripts with loosely typed helpers; migrating older Python code without annotations into tools; forgetting annotations on **kwargs-like helper parameters or defaults-only parameters.","solutions":["Add a type annotation to the parameter named in the message","Annotate every parameter of the function you pass to @tool / create_tool_from_function","If the parameter is Agent State, annotate it as State (it is then skipped)","If the parameter should not be exposed to the LLM, remove it from the signature or give it a module-level constant default outside the tool function"],"exampleFix":"// before\ndef search(query, limit=5):\n    ...\n\n// after\ndef search(query: str, limit: int = 5) -> dict:\n    ...","handlingStrategy":"validation","validationCode":"import inspect\n\ndef validate_tool_function(fn) -> list[str]:\n    return [\n        p.name\n        for p in inspect.signature(fn).parameters.values()\n        if p.annotation is inspect.Parameter.empty\n    ]\n\nmissing = validate_tool_function(search)\nassert not missing, f\"Parameters missing type hints: {missing}\"","typeGuard":"import inspect\n\ndef is_fully_annotated(fn) -> bool:\n    return all(\n        p.annotation is not inspect.Parameter.empty\n        for p in inspect.signature(fn).parameters.values()\n    )","tryCatchPattern":"try:\n    tool = create_tool_from_function(search)\nexcept ValueError as e:\n    # message names the offending parameter\n    raise TypeError(f\"Fix tool function signature: {e}\") from e","preventionTips":["Fully annotate every tool function's parameters and return type","Run mypy/ruff (ANN rules) so un-annotated functions fail CI before tool creation","Annotate Agent-injected params explicitly as State so they're skipped correctly"],"tags":["type-hints","tool-definition","python","validation"],"backgroundTag":"missing-type-annotation","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}