Hi @Jameskanyiri
The error means the runtime parameter is not being injected when your tool runs. There are two common causes and two clean fixes:
-
Cause A: Using @tool(..., args_schema=...) hides the runtime parameter from the tool’s inferred input schema, so the LangGraph ToolNode can’t detect it and doesn’t inject it.
-
Cause B: Invoking the tool outside of LangGraph’s ToolNode/agent flow (e.g., calling the function or tool directly) - injection only happens when tools are executed by the graph or the new v1 agents.
Pick one of the following fixes.
- Use ToolRuntime injection (remove args_schema and run via ToolNode/agent)
from langchain_core.tools import tool
from langchain.tools import ToolRuntime
from langgraph.types import Command
from langchain_core.messages import ToolMessage
@tool("site_selection", description=SITE_SELECTION_DESCRIPTION)
def handle_site_selection(
country: str,
location: str,
system_capacity_kw: float,
capex_per_kw: float,
search_radius_km: float,
max_sites: int,
runtime: ToolRuntime,
) -> Command:
tool_call_id = runtime.tool_call_id
return Command(update={"messages": [ToolMessage("site selection result", tool_call_id=tool_call_id)]})
Ensure the tool is executed by a LangGraph ToolNode (or the new v1 create_agent) so ToolRuntime is constructed and injected, giving you runtime.tool_call_id.
- Keep args_schema but inject only the tool_call_id
Important: if you keep args_schema, the injected parameter must be defined on that schema. Add tool_call_id to SiteSelectionInput annotated with InjectedToolCallId, then accept it in your function.
from typing_extensions import Annotated
from pydantic import BaseModel
from langchain_core.tools import InjectedToolCallId
class SiteSelectionInput(BaseModel):
country: str
location: str
system_capacity_kw: float
capex_per_kw: float
search_radius_km: float
max_sites: int
tool_call_id: Annotated[str, InjectedToolCallId] # injected by the runtime
from langchain_core.tools import tool
from langgraph.types import Command
from langchain_core.messages import ToolMessage
@tool("site_selection", description=SITE_SELECTION_DESCRIPTION, args_schema=SiteSelectionInput)
def handle_site_selection(
country: str,
location: str,
system_capacity_kw: float,
capex_per_kw: float,
search_radius_km: float,
max_sites: int,
tool_call_id: str, # can be plain str; it's annotated in the schema
) -> Command:
return Command(update={"messages": [ToolMessage("site selection result", tool_call_id=tool_call_id)]})
This keeps your custom schema and still injects the call id. You do not need a runtime parameter for this option. If you instead want to inject runtime while keeping args_schema, then the schema must include a runtime: ToolRuntime field (the injector detects a field named runtime or typed as ToolRuntime).
- Keep args_schema and inject runtime via the schema
If you want runtime while keeping a custom args_schema, define it on the schema and on the function. Also allow arbitrary types so Pydantic accepts ToolRuntime.
# Pydantic v2
from pydantic import BaseModel, ConfigDict
from langchain.tools import ToolRuntime
class SiteSelectionInput(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
country: str
location: str
system_capacity_kw: float
capex_per_kw: float
search_radius_km: float
max_sites: int
runtime: ToolRuntime # injected by ToolNode
# Pydantic v1
from pydantic import BaseModel
from langchain.tools import ToolRuntime
class SiteSelectionInput(BaseModel):
class Config:
arbitrary_types_allowed = True
country: str
location: str
system_capacity_kw: float
capex_per_kw: float
search_radius_km: float
max_sites: int
runtime: ToolRuntime
from langchain_core.tools import tool
from langgraph.types import Command
from langchain_core.messages import ToolMessage
@tool("site_selection", description=SITE_SELECTION_DESCRIPTION, args_schema=SiteSelectionInput)
def handle_site_selection(
country: str,
location: str,
system_capacity_kw: float,
capex_per_kw: float,
search_radius_km: float,
max_sites: int,
runtime: ToolRuntime, # must be accepted by the function
) -> Command:
tool_call_id = runtime.tool_call_id
return Command(update={"messages": [ToolMessage("site selection result", tool_call_id=tool_call_id)]})