langchain-ai/langchain · error · ValueError
Runnable must have an object schema.
Error message
Runnable must have an object schema.
What it means
Raised by the @tool decorator when the object being converted is a Runnable whose input JSON schema is not of type 'object'. Tools expose their inputs as named keyword arguments, so the Runnable must accept a single dict/TypedDict/pydantic-model input; scalar (str, int) or tuple inputs cannot be mapped to tool arguments.
Source
Thrown at libs/core/langchain_core/tools/convert.py:291
"""Create a decorator that takes a callable and returns a tool.
Args:
tool_name: The name that will be assigned to the tool.
Returns:
A function that takes a callable or `Runnable` and returns a tool.
"""
def _tool_factory(
dec_func: Callable[..., Any] | Runnable[Any, Any],
) -> BaseTool:
tool_description = description
if isinstance(dec_func, Runnable):
runnable = dec_func
if runnable.get_input_jsonschema().get("type") != "object":
msg = "Runnable must have an object schema."
raise ValueError(msg)
async def ainvoke_wrapper(
callbacks: Callbacks | None = None, **kwargs: Any
) -> Any:
return await runnable.ainvoke(kwargs, {"callbacks": callbacks})
def invoke_wrapper(
callbacks: Callbacks | None = None, **kwargs: Any
) -> Any:
return runnable.invoke(kwargs, {"callbacks": callbacks})
coroutine = ainvoke_wrapper
func = invoke_wrapper
schema: ArgsSchema | None = runnable.input_schema
tool_description = description or repr(runnable)
elif inspect.iscoroutinefunction(dec_func):
coroutine = dec_func
func = NoneView on GitHub (pinned to e32fa9a52e)
Solutions
- Give the Runnable a dict-like input: wrap it so InputType is a TypedDict or pydantic BaseModel, e.g. lambda x: chain(x['text']) with InputType typed as a TypedDict
- Or skip @tool and use Tool(func=lambda text: runnable.invoke(text)) for single-string inputs
- Check runnable.get_input_jsonschema() first and restructure until it returns {"type": "object", ...}
Example fix
# before
chain = prompt | llm # InputType: str
@tool
def my_tool(): ... # or tool(chain)
# after
class ChainInput(TypedDict):
question: str
def _run(x: ChainInput) -> str:
return (prompt | llm).invoke({"question": x["question"]})
chain = RunnableLambda(_run) # get_input_jsonschema() -> type: object
tool = chain.as_tool(name="answer") Defensive patterns
Strategy: type-guard
Validate before calling
def is_object_schema_runnable(runnable) -> bool:
try:
schema = runnable.get_input_jsonschema()
except Exception:
return False
return isinstance(schema, dict) and schema.get("type") == "object" Type guard
from langchain_core.runnables import Runnable
def can_convert_to_tool(runnable: object) -> bool:
return (
isinstance(runnable, Runnable)
and runnable.get_input_jsonschema().get("type") == "object"
) Try / catch
try:
t = runnable.as_tool()
except ValueError as e:
if "object schema" in str(e):
t = Tool(name="runnable", func=lambda **kw: runnable.invoke(kw),
description="Wrapped runnable")
else:
raise Prevention
- Type Runnable inputs as TypedDict or pydantic BaseModel when you plan to call .as_tool()
- Check runnable.get_input_jsonschema() during development before decorating
- Keep single-string-input runnables out of @tool; wrap them with RunnableLambda(typed_fn) first
When it happens
Trigger: @tool applied to a Runnable whose InputType is str (e.g. a bare prompt template or a lambda taking a string); runnable.as_tool() / convert_runnable_to_tool on a chain with InputType = int or a Union; a Runnable built with a func whose input schema is an array.
Common situations: Wrapping a prompt | llm chain that takes a plain string input; converting a parser or embedding-style Runnable into a tool; using .with_structured_output(...) on something whose input is not a dict.
Related errors
- Tool input must be str or dict. If dict, dict arguments must
- Runnable without name for tool constructor
- Name must be a string for tool constructor
- Too many arguments to single-input tool {self.name}.
- Invalid args_schema: expected BaseModel or dict, got {args_s
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/a11a920e433fce66.
Report an issue: GitHub.