Submission checklist
Package (Required)
Related Issues / PRs
Closest I found, neither a duplicate: #31035 (fields with an alias silently dropped from the
schema — same family, different cause) and #33300 (InjectedState treated as required, but at the
LangGraph CLI layer).
Reproduction Steps / Example Code (Python)
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from pydantic import BaseModel
from typing_extensions import Annotated
class Container(BaseModel):
rows: list["Row"] = [] # "Row" is declared below, after the tool is decorated
@tool
def my_tool(real_arg: Annotated[str, "a real argument"], container: Container):
"""A tool whose schema depends on a forward reference that is not resolvable yet."""
return "ok"
class Row(BaseModel):
name: str
print(my_tool.get_input_schema().__pydantic_complete__)
print(list(my_tool.args))
print(convert_to_openai_tool(my_tool)["function"]["parameters"])
# Output:
# False
# []
# {'properties': {}, 'type': 'object'}
#
# The tool is handed to the model with NO parameters at all, and nothing is logged or raised.
# Moving `class Row` above `Container` fixes it -- but nothing points you there.
#
# A plain @dataclass behaves the same. Reproduced on langchain-core 0.3.29, 0.3.75, 1.0.0,
# 1.2.6, 1.2.22, 1.2.27 and 1.5.1, and on Python 3.10 / 3.11 / 3.12 / 3.13 (pydantic 2.12.5).
# Both langchain-google-genai and langchain-aws send the same empty declaration.
Error Message and Stack Trace (if applicable)
# Nothing is raised at schema-build time -- that is the problem. It surfaces later,
# because the advertised schema and the validated schema disagree:
#
# my_tool.invoke({"name": "my_tool", "args": {}, "id": "1", "type": "tool_call"})
pydantic_core._pydantic_core.ValidationError: 2 validation errors for my_tool
real_arg
Field required [type=missing, input_value={}, input_type=dict]
container
Field required [type=missing, input_value={}, input_type=dict]
Description
To be clear about scope: the unresolved forward reference is a mistake in user code. The bug is that langchain-core degrades silently instead of surfacing it — the tool is still handed to the model, just with zero parameters.
What makes this hard to find is that the same mistake is already a hard error one level up. If the unresolved name sits directly in the tool signature:
@tool
def my_tool(real_arg: Annotated[str, "a real argument"], rows: list["Row"]):
...
File "langchain_core/tools/base.py", line 329, in create_schema_from_function
NameError: name 'Row' is not defined
Nested one level deeper, the same unresolvable name produces an empty schema and silence. That inconsistency is the core of this report.
Mechanically: the incomplete model degrades inspect.signature() to (**data: Any), get_all_basemodel_annotations() therefore returns {}, and tool_call_schema reads that as "this tool has no arguments" rather than "the schema could not be resolved".
This is easy to hit with LangGraph, where InjectedState parameters are typically typed with a rich application object whose entire type graph must be resolvable at decoration time. In our case 12 of 13 tools in one module were affected for months without anyone noticing: an empty schema fails differently depending on the model — one provider invented field names, another sent {} — so the symptom looks like a flaky model rather than a broken schema.
Suggested fix
Fail loudly, matching the direct-signature case. Any of:
- Warn or raise in
BaseTool.tool_call_schema when the subset model ends up empty while args_schema.model_fields is not — those two disagreeing is always a bug.
- Warn when
args_schema.__pydantic_complete__ is False.
- Call
args_schema.model_rebuild() once before introspection. This is enough to fix the example above:
my_tool.args_schema.model_rebuild(force=True)
# -> convert_to_openai_tool now returns ['real_arg', 'container']
Happy to open a PR for whichever direction you prefer.
System Info
System Information
OS: Linux
OS Version: #1 SMP PREEMPT_DYNAMIC Thu Jun 18 21:54:43 UTC 2026
Python Version: 3.12.11 (main, Jun 29 2025, 16:24:38) [Clang 20.1.4 ]
Package Information
langchain_core: 1.2.27
langsmith: 0.10.10
langchain_google_genai: 4.2.1
langgraph_sdk: 0.3.15
Optional packages not installed
deepagents
deepagents-cli
Other Dependencies
anyio: 4.14.2
distro: 1.9.0
filetype: 1.2.0
google-genai: 1.75.0
httpx: 0.28.1
jsonpatch: 1.33
orjson: 3.11.9
packaging: 24.2
pydantic: 2.12.5
pyyaml: 6.0.3
requests: 2.34.2
requests-toolbelt: 1.0.0
sniffio: 1.3.1
tenacity: 9.1.4
typing-extensions: 4.16.0
uuid-utils: 0.17.0
websockets: 16.1.1
xxhash: 3.8.1
zstandard: 0.25.0
Also reproduced on langchain-core 0.3.29, 0.3.75, 1.0.0, 1.2.6, 1.2.22 and 1.5.1,
and on Python 3.10.18, 3.11.13 and 3.13.5.
Submission checklist
Package (Required)
Related Issues / PRs
Closest I found, neither a duplicate: #31035 (fields with an
aliassilently dropped from theschema — same family, different cause) and #33300 (
InjectedStatetreated as required, but at theLangGraph CLI layer).
Reproduction Steps / Example Code (Python)
Error Message and Stack Trace (if applicable)
Description
To be clear about scope: the unresolved forward reference is a mistake in user code. The bug is that
langchain-coredegrades silently instead of surfacing it — the tool is still handed to the model, just with zero parameters.What makes this hard to find is that the same mistake is already a hard error one level up. If the unresolved name sits directly in the tool signature:
Nested one level deeper, the same unresolvable name produces an empty schema and silence. That inconsistency is the core of this report.
Mechanically: the incomplete model degrades
inspect.signature()to(**data: Any),get_all_basemodel_annotations()therefore returns{}, andtool_call_schemareads that as "this tool has no arguments" rather than "the schema could not be resolved".This is easy to hit with LangGraph, where
InjectedStateparameters are typically typed with a rich application object whose entire type graph must be resolvable at decoration time. In our case 12 of 13 tools in one module were affected for months without anyone noticing: an empty schema fails differently depending on the model — one provider invented field names, another sent{}— so the symptom looks like a flaky model rather than a broken schema.Suggested fix
Fail loudly, matching the direct-signature case. Any of:
BaseTool.tool_call_schemawhen the subset model ends up empty whileargs_schema.model_fieldsis not — those two disagreeing is always a bug.args_schema.__pydantic_complete__isFalse.args_schema.model_rebuild()once before introspection. This is enough to fix the example above:Happy to open a PR for whichever direction you prefer.
System Info
System Information
Package Information
Optional packages not installed
Other Dependencies
Also reproduced on langchain-core 0.3.29, 0.3.75, 1.0.0, 1.2.6, 1.2.22 and 1.5.1,
and on Python 3.10.18, 3.11.13 and 3.13.5.