run-llama/llama_index · error · ValueError
fn_schema is None.
Error message
fn_schema is None.
What it means
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.
Source
Thrown at llama-index-core/llama_index/core/tools/types.py:52
"properties": {
"input": {"title": "input query string", "type": "string"},
},
"required": ["input"],
}
else:
parameters = self.fn_schema.model_json_schema()
parameters = {
k: v
for k, v in parameters.items()
if k in ["type", "properties", "required", "definitions", "$defs"]
}
return parameters
@property
def fn_schema_str(self) -> str:
"""Get fn schema as string."""
if self.fn_schema is None:
raise ValueError("fn_schema is None.")
parameters = self.get_parameters_dict()
return json.dumps(parameters, ensure_ascii=False)
def get_name(self) -> str:
"""Get name."""
if self.name is None:
raise ValueError("name is None.")
return self.name
def _sanitize_name(self, name: Optional[str]) -> Optional[str]:
"""
Sanitize name to match OpenAI's function name requirements.
OpenAI requires function names to match ^[a-zA-Z0-9_-]+$.
Generic Pydantic models like GenericModel[int] contain brackets
which are not allowed.
"""
if name is None:View on GitHub (pinned to afd0fef371)
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.
Example fix
# before
tool = FunctionTool.from_defaults(fn=my_fn) # my_fn has no annotations
print(tool.metadata.fn_schema_str) # ValueError
# after
from pydantic import BaseModel
class MyFnArgs(BaseModel):
query: str
tool = FunctionTool.from_defaults(fn=my_fn, fn_schema=MyFnArgs)
print(tool.metadata.fn_schema_str) Defensive patterns
Strategy: validation
Validate before calling
def safe_fn_schema_str(tool) -> str:
if tool.metadata.fn_schema is None:
return '' # or raise your own explicit config error
return tool.metadata.fn_schema_str Type guard
def tool_has_schema(tool) -> bool:
return getattr(getattr(tool, 'metadata', None), 'fn_schema', None) is not None Try / catch
try:
schema = tool.metadata.fn_schema_str
except ValueError as e:
if 'fn_schema is None' in str(e):
raise ValueError(f"Tool {tool!r} needs fn_schema or typed params") from e
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Tool name cannot be None
- name is None.
- Tool description exceeds maximum length of 1024 characters.
- Invalid additional field info: {field_info}. Must be a tuple
- There are {len(self.selections)} selections, please use .ind
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/9f72b5ff9dd9384b.
Report an issue: GitHub.