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

  1. Give the underlying function complete type hints so FunctionTool.from_defaults can auto-generate the schema.
  2. Pass an explicit schema: FunctionTool.from_defaults(fn=fn, fn_schema=MyParamsModel).
  3. 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

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


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/9f72b5ff9dd9384b. Report an issue: GitHub.