langchain - ✅(Solved) Fix Args with default_factory in a tool's args_schema should not be required [3 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#35547Fetched 2026-04-08 00:25:43
View on GitHub
Comments
0
Participants
1
Timeline
10
Reactions
0
Participants
Timeline (top)
labeled ×4cross-referenced ×3closed ×1issue_type_added ×1

Fix Action

Fixed

PR fix notes

PR #35550: fix(core): preserve default_factory when generating tool call schema

Description (problem / solution / changelog)

Fields with default_factory in a tool's args_schema are incorrectly marked as required in the generated schema. The issue is in _create_subset_model_v2 — it copies field.default but drops field.default_factory, so Pydantic treats the rebuilt field as required. The fix forwards default_factory alongside default when reconstructing FieldInfo.

Fixes #35547

Changed files

  • libs/core/langchain_core/utils/pydantic.py (modified, +6/-1)
  • libs/core/tests/unit_tests/test_tools.py (modified, +17/-0)
  • libs/core/tests/unit_tests/utils/test_pydantic.py (modified, +19/-0)

PR #35557: fix(core): preserve default_factory when creating subset models

Description (problem / solution / changelog)

Summary

  • _create_subset_model_v2 only copied field.default when rebuilding FieldInfo, ignoring field.default_factory
  • This caused fields with default_factory (e.g. Field(default_factory=list)) to be incorrectly marked as "required" in generated tool schemas
  • Fix: pass default_factory=field.default_factory alongside default=field.default in the FieldInfoV2 constructor

Files changed

  • libs/core/langchain_core/utils/pydantic.py — add default_factory parameter to FieldInfoV2() call in _create_subset_model_v2
  • libs/core/tests/unit_tests/utils/test_pydantic.py — add test_with_default_factory covering schema correctness and runtime instantiation

Test plan

  • test_with_default_factory — verifies only truly required fields appear in "required", and that default/default_factory values work at runtime
  • Full test_pydantic.py suite: 10 passed
  • Full test_tools.py suite: 161 passed

Closes #35547

🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/utils/pydantic.py (modified, +5/-1)
  • libs/core/tests/unit_tests/utils/test_pydantic.py (modified, +22/-0)

PR #35659: core: preserve default_factory in tool call schema so fields are not incorrectly required

Description (problem / solution / changelog)

Description

Fixes a bug where tool schema fields defined with default_factory (e.g. Field(default_factory=list)) are incorrectly marked as required in the generated OpenAI function/tool schema.

Root cause

In _create_subset_model_v2 (libs/core/langchain_core/utils/pydantic.py), the subset model's FieldInfo is created with only default=field.default:

field_info = FieldInfoV2(description=description, default=field.default)

When a field uses default_factory, field.default is PydanticUndefined, so the new field has no default value and appears as required in the schema.

Fix

Check for default_factory first and pass it through to the new FieldInfo. Only fall back to default when no factory is set.

Before

{
  "required": ["names"],  // names has default_factory=list, should NOT be required
  ...
}

After

{
  "required": ["required_arg"],  // only truly required fields
  ...
}

Issue

Fixes #35547

Tests

Verified with reproduction script from the issue.

Changed files

  • libs/core/langchain_core/utils/pydantic.py (modified, +6/-1)

Code Example

import json

from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from pydantic import BaseModel, Field


class Args(BaseModel):
    """Hello"""
    names: list[str] = Field(default_factory=list, description="Some names")

@tool(args_schema=Args)
def some_func(name: list[str] = None):
    pass


print(json.dumps(convert_to_openai_tool(some_func), indent=2))

---

{
  "type": "function",
  "function": {
    "name": "some_func",
    "description": "Hello",
    "parameters": {
      "properties": {
        "names": {
          "description": "Some names",
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      },
      "required": [
        "names"
      ],
      "type": "object"
    }
  }
}
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a feature request, not a bug report or usage question.
  • I added a clear and descriptive title that summarizes the feature request.
  • I used the GitHub search to find a similar feature request and didn't find it.
  • I checked the LangChain documentation and API reference to see if this feature already exists.
  • This is not related to the langchain-community package.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Feature Description

When a tool is defined to have a Pydantic-based args_schema with default_factory, the tool schema generated should not put that arg are "required"

Use Case

As in https://github.com/langchain-ai/langchain/issues/34384, langchain currently will populate args with default and default_factory. But the tool schema generated still put theses fields in "required", forcing the LLM to generate these fields.

Here is an example snippet:

import json

from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from pydantic import BaseModel, Field


class Args(BaseModel):
    """Hello"""
    names: list[str] = Field(default_factory=list, description="Some names")

@tool(args_schema=Args)
def some_func(name: list[str] = None):
    pass


print(json.dumps(convert_to_openai_tool(some_func), indent=2))

Outputs(note names in required):

{
  "type": "function",
  "function": {
    "name": "some_func",
    "description": "Hello",
    "parameters": {
      "properties": {
        "names": {
          "description": "Some names",
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      },
      "required": [
        "names"
      ],
      "type": "object"
    }
  }
}

Proposed Solution

Langchain dynamically create a new model when generating tool schema in tool_call_schema, currently it only copies field.default not field.default_factory thus making the generated schema incorrect. https://github.com/langchain-ai/langchain/blob/e50625e7c3cac04be68ae095bee030e0c321ef34/libs/core/langchain_core/utils/pydantic.py#L245

Alternatives Considered

Using Field(default=[]) works, but it maybe a little misleading.

Additional Context

No response

extent analysis

Fix Plan

Step 1: Update tool_call_schema to handle default_factory

Update the tool_call_schema function in langchain_core/utils/pydantic.py to handle default_factory fields. You can do this by adding a check for default_factory and using its value instead of default when generating the schema.

def tool_call_schema(tool, args_schema):
    # ...
    for field in args_schema.__fields__.values():
        if field.field_info.default_factory is not None:
            default = field.field_info.default_factory()
        else:
            default = field.field_info.default
        # ...

Step 2: Test the updated tool_call_schema function

Create a test case to verify that the updated tool_call_schema function correctly handles default_factory fields.

import pytest
from langchain_core.utils.pydantic import tool_call_schema

class Args(BaseModel):
    names: list[str] = Field(default_factory=list, description="Some names")

@tool(args_schema=Args)
def some_func(name: list[str] = None):
    pass

schema = tool_call_schema(some_func, Args)
print(json.dumps(schema, indent=2))

Step 3: Verify the fix

Verify that the updated tool_call_schema function correctly generates the schema without including names in the required list.

{
  "type": "function",
  "function": {
    "name": "some_func",
    "description": "Hello",
    "parameters": {
      "properties": {
        "names": {
          "description": "Some names",
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      },
      "type": "object"
    }
  }
}

Verification

To verify that the fix worked, you can run

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING