langchain - ✅(Solved) Fix Add `ToolArgValidationMiddleware` — a new agent middleware that validates LLM-generated tool-call arguments [2 pull requests, 3 comments, 3 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#36700Fetched 2026-04-15 06:19:56
View on GitHub
Comments
3
Participants
3
Timeline
11
Reactions
0
Author
Timeline (top)
commented ×3labeled ×3cross-referenced ×2issue_type_added ×1

Error Message

When validation fails, the middleware appends error ToolMessages describing what went wrong and re-invokes the model so it can self-correct. This retry loop runs entirely inside the model node — only the final valid AIMessage ever enters the graph state. 4. On failure, build ToolMessages with human-readable error descriptions, append them to the request, and re-invoke the model

  • Tool-level validation: Let each tool validate its own input and raise errors. This works but the error surfaces after the tool node, requiring a full agent loop iteration to retry. The middleware catches errors earlier and retries within the model node.
  • 57 unit tests covering: initialization, strip logic, Pydantic validation, JSON Schema validation, error formatting, sync/async retry loops, batch mixed valid/invalid tool calls, schema resolution caching, and end-to-end tests with create_agent + FakeToolCallingModel

Root Cause

Currently, users must implement their own validation logic or accept tool-level failures and hope the agent recovers. A built-in middleware provides a standard, reusable solution that validates and retries at the model boundary before tool execution or HITL happens (it makes sense, because you don't want provide obviously incorrect args to the user, that can be detected and fixed during inner validation against the tool schema).

PR fix notes

PR #36722: feat(langchain): add ToolArgValidationMiddleware for model-side validation

Description (problem / solution / changelog)

Fixes #36700

Adds ToolArgValidationMiddleware to validate LLM-generated tool-call arguments against Pydantic and JSON schemas at the model boundary. This enables the model to self-correct invalid arguments via an internal retry loop before execution, reducing runtime errors in tool nodes.

How I verified:

  • Ran make format, make lint, and make test within libs/langchain_v1.
  • All 14 tests in test_tool_arg_validation.py passed, covering Pydantic v1/v2, JSON Schema, and E2E AgentExecutor flows.

Changed files

  • libs/langchain_v1/langchain/agents/middleware/__init__.py (modified, +2/-0)
  • libs/langchain_v1/langchain/agents/middleware/tool_arg_validation.py (added, +417/-0)
  • libs/langchain_v1/tests/unit_tests/agents/middleware/implementations/test_tool_arg_validation.py (added, +665/-0)

PR #36699: feat(langchain): add tool argument validation middleware

Description (problem / solution / changelog)

Add ToolArgValidationMiddleware — a new agent middleware that validates LLM-generated tool-call arguments against each tool's schema before the tool node fires, retrying with error feedback when validation fails.

Feature: https://github.com/langchain-ai/langchain/issues/36700 #

What it does

Intercepts model responses via wrap_model_call / awrap_model_call and validates tool-call arguments against their schemas:

  • Pydantic-based tools (@tool decorator): validated with BaseModel.model_validate
  • MCP / dict-schema tools: validated with jsonschema (Draft7Validator by default, configurable via json_schema_validator_class)

On validation failure, error ToolMessages are appended to the conversation and the model is re-invoked (up to max_retries times). The retry loop runs entirely inside the model node, so only the final valid AIMessage enters the graph state.

Configuration

ToolArgValidationMiddleware(
    max_retries=2,              # validation-retry cycles per invocation
    strip_empty_values=True,    # recursively strip None/{}/[] before validation
    json_schema_validator_class=None,  # optional: e.g. Draft202012Validator
)

How it was verified

  • make format — clean
  • make lint — ruff check + ruff format --diff + mypy strict all pass
  • make test — 53 passed, 4 skipped (jsonschema not installed in test env)
  • Test coverage: initialization, strip logic, Pydantic validation, JSON Schema validation, error formatting, sync/async retry loops, batch mixed valid/invalid, schema resolution caching, end-to-end with create_agent + FakeToolCallingModel

AI disclosure

This contribution was developed with assistance from an AI coding agent (GitHub Copilot / Claude).

Social handles (optional)

LinkedIn: https://www.linkedin.com/in/sergey-borisov-457325233/

Changed files

  • libs/langchain_v1/langchain/agents/middleware/__init__.py (modified, +2/-0)
  • libs/langchain_v1/langchain/agents/middleware/tool_arg_validation.py (added, +506/-0)
  • libs/langchain_v1/pyproject.toml (modified, +1/-1)
  • libs/langchain_v1/tests/unit_tests/agents/middleware/implementations/test_tool_arg_validation.py (added, +1094/-0)
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

Feature Description

Add a ToolArgValidationMiddleware to the LangChain agent middleware system that validates LLM-generated tool-call arguments against each tool's schema before tool execution.

The middleware intercepts model responses via wrap_model_call / awrap_model_call and checks every tool call's arguments against the tool's declared schema. It supports two validation paths:

  • Pydantic-based tools (created with @tool or having a BaseModel args_schema): validated using BaseModel.model_validate
  • MCP / dict-schema tools (where args_schema is a raw JSON Schema dict): validated using jsonschema (soft dependency, Draft7Validator by default, configurable)

When validation fails, the middleware appends error ToolMessages describing what went wrong and re-invokes the model so it can self-correct. This retry loop runs entirely inside the model node — only the final valid AIMessage ever enters the graph state.

Configurable parameters:

  • max_retries (default 2) — number of validation-retry cycles per model invocation
  • strip_empty_values (default True) — recursively remove keys with None, {}, or [] values before validation (common LLM hallucination pattern)
  • json_schema_validator_class (default NoneDraft7Validator) — allows overriding the JSON Schema validator class for dict-based schemas

Use Case

Use Case

LLMs frequently generate tool calls with malformed arguments — missing required fields, wrong types, hallucinated empty values, or extra keys. Without validation, these bad arguments reach the tool node and cause runtime errors or silent data corruption that is hard to debug.

This is especially problematic with:

  • MCP tools that have complex JSON Schemas the LLM hasn't been fine-tuned on
  • Agentic loops where a single bad tool call can derail a multi-step task
  • Human-in-the-loop workflows where invalid arguments would be presented to users for approval before anyone notices they're malformed

Currently, users must implement their own validation logic or accept tool-level failures and hope the agent recovers. A built-in middleware provides a standard, reusable solution that validates and retries at the model boundary before tool execution or HITL happens (it makes sense, because you don't want provide obviously incorrect args to the user, that can be detected and fixed during inner validation against the tool schema).

Proposed Solution

Proposed Solution

Implement ToolArgValidationMiddleware as a new middleware in langchain.agents.middleware, following the same patterns as existing middlewares (ModelRetryMiddleware, ToolRetryMiddleware, ToolCallLimitMiddleware):

  1. Subclass AgentMiddleware with wrap_model_call and awrap_model_call
  2. Lazily resolve tool schemas from request.tools on first call (cached thereafter)
  3. After each model response, validate every tool_call in the AIMessage against its schema
  4. On failure, build ToolMessages with human-readable error descriptions, append them to the request, and re-invoke the model
  5. After max_retries exhausted, pass through the last response as-is (fail open)

Key design decisions:

  • jsonschema is a soft dependency — only imported when dict-schema tools are present, so it doesn't affect users who only use Pydantic tools
  • Schema resolution is lazy and cached — no overhead on subsequent calls
  • The middleware strips empty values before validation by default, since LLMs commonly hallucinate None/{}/[] for optional fields
  • Unknown tools (not in the schema map) pass through without validation

Alternatives Considered

Alternatives Considered

  • Tool-level validation: Let each tool validate its own input and raise errors. This works but the error surfaces after the tool node, requiring a full agent loop iteration to retry. The middleware catches errors earlier and retries within the model node.
  • Pydantic-only validation: Skip jsonschema support and only validate BaseModel schemas. This would miss MCP tools entirely, which are increasingly common.
  • Strict mode / structured output: Some providers support constrained decoding. This doesn't cover all providers or all schema types and isn't available for MCP tools with dict schemas.
  • Post-processing in a custom node: Users could add a validation node after the model node. This requires graph modification and doesn't benefit from the middleware's retry-within-model-call pattern.

Additional Context

Additional Context

  • Follows existing middleware conventions in langchain.agents.middleware — same base class, same wrap_model_call/awrap_model_call pattern, keyword-only __init__ parameters
  • 57 unit tests covering: initialization, strip logic, Pydantic validation, JSON Schema validation, error formatting, sync/async retry loops, batch mixed valid/invalid tool calls, schema resolution caching, and end-to-end tests with create_agent + FakeToolCallingModel
  • All checks pass: make format, make lint (ruff + mypy strict), make test
  • Related prior art: ModelRetryMiddleware handles model-level retries on exceptions; this middleware handles argument-level validation retries on schema violations

extent analysis

TL;DR

Implement the proposed ToolArgValidationMiddleware to validate LLM-generated tool-call arguments against each tool's schema before tool execution.

Guidance

  • Review the proposed solution and existing middleware conventions in langchain.agents.middleware to ensure consistency.
  • Implement the ToolArgValidationMiddleware class, focusing on lazy schema resolution, validation, and retry logic.
  • Test the middleware thoroughly, covering various scenarios, including Pydantic and JSON Schema validation, error handling, and retry loops.
  • Consider the trade-offs between the proposed solution and alternative approaches, such as tool-level validation or post-processing in a custom node.

Example

class ToolArgValidationMiddleware(AgentMiddleware):
    def __init__(self, max_retries: int = 2, strip_empty_values: bool = True, json_schema_validator_class: type = None):
        # Initialize middleware with configurable parameters
        pass

    def wrap_model_call(self, model_call):
        # Implement validation and retry logic for model calls
        pass

Notes

The proposed solution seems well-structured, but its implementation details and potential edge cases should be carefully considered. The use of a soft dependency for jsonschema and lazy schema resolution are notable design decisions that may impact performance and usability.

Recommendation

Apply the proposed solution, as it provides a standard, reusable, and well-structured approach to validating LLM-generated tool-call arguments. This solution aligns with existing middleware conventions and addresses the need for early validation and retry logic within the model node.

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