{"record":{"id":"9f72b5ff9dd9384b","repo":"run-llama/llama_index","slug":"fn-schema-is-none","errorCode":null,"errorMessage":"fn_schema is None.","messagePattern":"fn_schema is None\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/tools/types.py","lineNumber":52,"sourceCode":"                \"properties\": {\n                    \"input\": {\"title\": \"input query string\", \"type\": \"string\"},\n                },\n                \"required\": [\"input\"],\n            }\n        else:\n            parameters = self.fn_schema.model_json_schema()\n            parameters = {\n                k: v\n                for k, v in parameters.items()\n                if k in [\"type\", \"properties\", \"required\", \"definitions\", \"$defs\"]\n            }\n        return parameters\n\n    @property\n    def fn_schema_str(self) -> str:\n        \"\"\"Get fn schema as string.\"\"\"\n        if self.fn_schema is None:\n            raise ValueError(\"fn_schema is None.\")\n        parameters = self.get_parameters_dict()\n        return json.dumps(parameters, ensure_ascii=False)\n\n    def get_name(self) -> str:\n        \"\"\"Get name.\"\"\"\n        if self.name is None:\n            raise ValueError(\"name is None.\")\n        return self.name\n\n    def _sanitize_name(self, name: Optional[str]) -> Optional[str]:\n        \"\"\"\n        Sanitize name to match OpenAI's function name requirements.\n\n        OpenAI requires function names to match ^[a-zA-Z0-9_-]+$.\n        Generic Pydantic models like GenericModel[int] contain brackets\n        which are not allowed.\n        \"\"\"\n        if name is None:","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/tools/types.py#L34-L70","documentation":"ToolMetadata.fn_schema_str is a property that JSON-serializes the tool's parameter schema; the schema can only be rendered if fn_schema (a Pydantic model) was set. When fn_schema is None the property raises ValueError('fn_schema is None.') instead of returning an empty string, so callers that inspect tool schemas fail loudly.","triggerScenarios":"Accessing tool.metadata.fn_schema_str (directly or via logging/serialization code that dumps tool metadata) on a tool created without fn_schema - common because FunctionTool.from_defaults only infers a schema from the function's type hints, and tools built from callables without full annotations end up with fn_schema=None.","commonSituations":"Custom tools whose function has no (or partial) type hints; AgentWorkflow / observability code that reads fn_schema_str for every registered tool; serializing tools to JSON for an OpenAI-compatible endpoint that requires a parameters string.","solutions":["Give the underlying function complete type hints so FunctionTool.from_defaults can auto-generate the schema.","Pass an explicit schema: FunctionTool.from_defaults(fn=fn, fn_schema=MyParamsModel).","Guard reads: use getattr-style checks or test tool.metadata.fn_schema is None before accessing fn_schema_str."],"exampleFix":"# before\n tool = FunctionTool.from_defaults(fn=my_fn)  # my_fn has no annotations\n print(tool.metadata.fn_schema_str)  # ValueError\n\n# after\nfrom pydantic import BaseModel\n\nclass MyFnArgs(BaseModel):\n    query: str\n\n tool = FunctionTool.from_defaults(fn=my_fn, fn_schema=MyFnArgs)\n print(tool.metadata.fn_schema_str)","handlingStrategy":"validation","validationCode":"def safe_fn_schema_str(tool) -> str:\n    if tool.metadata.fn_schema is None:\n        return ''  # or raise your own explicit config error\n    return tool.metadata.fn_schema_str","typeGuard":"def tool_has_schema(tool) -> bool:\n    return getattr(getattr(tool, 'metadata', None), 'fn_schema', None) is not None","tryCatchPattern":"try:\n    schema = tool.metadata.fn_schema_str\nexcept ValueError as e:\n    if 'fn_schema is None' in str(e):\n        raise ValueError(f\"Tool {tool!r} needs fn_schema or typed params\") from e\n    raise","preventionTips":["Type-annotate every tool function parameter so from_defaults infers a schema.","Pass fn_schema=MyArgsModel explicitly for tools with dynamic signatures.","Guard any generic serializer that reads fn_schema_str across mixed tool lists."],"tags":["tool-metadata","schema","pydantic","value-error"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}