langchain-ai/langchain · error · ValueError
When tool includes an InjectedToolCallId argument, tool must
Error message
When tool includes an InjectedToolCallId argument, tool must always be invoked with a full model ToolCall of the form: {'args': {...}, 'name': '...', 'type': 'tool_call', 'tool_call_id': '...'} What it means
If a tool's `args_schema` declares a field annotated `InjectedToolCallId`, langchain treats the tool as tool-calling-aware and requires invocation to carry the originating `ToolCall` (specifically a non-None `tool_call_id`), which it injects into that field. Invoking such a tool without `tool_call_id` (e.g. plain dict input, or `tool.invoke({'x': 1})`) raises this ValueError before validation completes.
Source
Thrown at libs/core/langchain_core/tools/base.py:834
return tool_input
if input_args is not None:
if isinstance(input_args, dict):
return tool_input
result: BaseModel | BaseModelV1
if issubclass(input_args, BaseModel):
# Check args_schema for InjectedToolCallId
for k, v in get_all_basemodel_annotations(input_args).items():
if _is_injected_arg_type(v, injected_type=InjectedToolCallId):
if tool_call_id is None:
msg = (
"When tool includes an InjectedToolCallId "
"argument, tool must always be invoked with a full "
"model ToolCall of the form: {'args': {...}, "
"'name': '...', 'type': 'tool_call', "
"'tool_call_id': '...'}"
)
raise ValueError(msg)
tool_input[k] = tool_call_id
result_v2 = input_args.model_validate(tool_input)
result_dict = result_v2.model_dump()
provided_fields = result_v2.model_fields_set
result = result_v2
elif issubclass(input_args, BaseModelV1):
# Check args_schema for InjectedToolCallId
for k, v in get_all_basemodel_annotations(input_args).items():
if _is_injected_arg_type(v, injected_type=InjectedToolCallId):
if tool_call_id is None:
msg = (
"When tool includes an InjectedToolCallId "
"argument, tool must always be invoked with a full "
"model ToolCall of the form: {'args': {...}, "
"'name': '...', 'type': 'tool_call', "
"'tool_call_id': '...'}"
)
raise ValueError(msg)View on GitHub (pinned to e32fa9a52e)
Solutions
- Invoke with a full tool call object or dict: `tool.invoke({'args': {...}, 'name': 't', 'type': 'tool_call', 'tool_call_id': 'abc123'})` (or a `ToolCall` pydantic object).
- In tests, fabricate the tool call with a dummy id: `ToolCall(name='t', args={...}, id='test-id', type='tool_call')`.
- If you truly don't need the call id, remove the `InjectedToolCallId` annotation from the schema.
Example fix
# before
tool.invoke({'query': 'hello'}) # schema has InjectedToolCallId field
# after
tool.invoke(
{'name': 'search', 'args': {'query': 'hello'}, 'type': 'tool_call', 'tool_call_id': 'call_123'}
) Defensive patterns
Strategy: validation
Validate before calling
from typing import get_type_hints
from langchain_core.tools import InjectedToolCallId
def requires_tool_call_id(tool) -> bool:
schema = tool.args_schema
if schema is None or isinstance(schema, dict):
return False
hints = get_type_hints(schema)
return any(
getattr(h, '__metadata__', ()) and any(
isinstance(m, type) and issubclass(m, InjectedToolCallId)
for m in h.__metadata__
)
for h in hints.values() if hasattr(h, '__metadata__')
)
# before direct invocation
if requires_tool_call_id(tool):
assert tool_call_id is not None, 'this tool needs a full ToolCall' Type guard
def is_full_tool_call(obj) -> bool:
if isinstance(obj, dict):
return obj.get('type') == 'tool_call' and bool(obj.get('tool_call_id')) and 'args' in obj
return getattr(obj, 'type', None) == 'tool_call' and bool(getattr(obj, 'id', None)) Try / catch
try:
out = tool.invoke(tool_input)
except ValueError as e:
if 'InjectedToolCallId' in str(e):
out = tool.invoke({
'name': tool.name, 'args': tool_input, 'type': 'tool_call',
'tool_call_id': f'call_{uuid4().hex[:8]}',
})
else:
raise Prevention
- Invoke InjectedToolCallId tools with full ToolCall objects carrying an id.
- In tests, fabricate dummy tool_call_ids rather than calling with bare args.
- Only annotate a field InjectedToolCallId if the tool is used inside a tool-calling agent loop.
When it happens
Trigger: Defining `class Args(BaseModel): tool_call_id: Annotated[str, InjectedToolCallId]` and calling `tool.invoke({'query': 'hi'})` directly; passing a `ToolCall` dict that lacks `'tool_call_id'`; manually replaying tool calls without preserving the id.
Common situations: Testing InjectedToolCallId tools outside a tool-calling agent loop; copying tool-call examples that build partial `ToolCall` dicts; calling artifact-style tools (which need the call id to store artifacts) via `.invoke()` with only the args dict.
Related errors
- Arguments 'observation' & 'llm_output' are required if 'send
- Function {raw_tool_call['function']['name']} arguments: {ar
- {exceptions joined with '\n\n'}
- This output parser can only be used with a chat generation.
- Tool arguments must be specified as a dict, received: {res['
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/eaa8607f090da9a1.
Report an issue: GitHub.