langchain - ✅(Solved) Fix ModelRequest.override(messages=...) rejects list[BaseMessage] due to list invariance [5 pull requests, 6 comments, 4 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#35971Fetched 2026-04-08 00:47:51
View on GitHub
Comments
6
Participants
4
Timeline
21
Reactions
0
Timeline (top)
commented ×6referenced ×6cross-referenced ×5labeled ×3

_ModelRequestOverrides.messages is typed as list[AnyMessage]. Since list is invariant in Python's type system, passing list[BaseMessage] (common when filtering/transforming messages) fails type checking even though every BaseMessage is a valid message.

A minimal fix would change messages: list[AnyMessage] to messages: Sequence[BaseMessage] in _ModelRequestOverrides. Sequence is covariant (read-only), which correctly models the input-only usage in override().

However, since override() passes values through to dataclasses.replace(), the ModelRequest dataclass field itself (messages: list[AnyMessage]) may also need to change to Sequence[BaseMessage] for full consistency — otherwise mypy flags the replace() call. The same pattern was already used for ModelResponse.result (line 282: result: list[BaseMessage]).

Error Message

from langchain.agents.middleware import before_model, ModelRequest from langchain_core.messages import BaseMessage, AIMessage, HumanMessage

@before_model def trim_messages(request: ModelRequest) -> ModelRequest: # Filter messages — result is list[BaseMessage] trimmed: list[BaseMessage] = [ m for m in request.messages if isinstance(m, (AIMessage, HumanMessage)) ] # Pyright error: list[BaseMessage] is not assignable to list[AnyMessage] return request.override(messages=trimmed)

Root Cause

_ModelRequestOverrides.messages is typed as list[AnyMessage]. Since list is invariant in Python's type system, passing list[BaseMessage] (common when filtering/transforming messages) fails type checking even though every BaseMessage is a valid message.

A minimal fix would change messages: list[AnyMessage] to messages: Sequence[BaseMessage] in _ModelRequestOverrides. Sequence is covariant (read-only), which correctly models the input-only usage in override().

However, since override() passes values through to dataclasses.replace(), the ModelRequest dataclass field itself (messages: list[AnyMessage]) may also need to change to Sequence[BaseMessage] for full consistency — otherwise mypy flags the replace() call. The same pattern was already used for ModelResponse.result (line 282: result: list[BaseMessage]).

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Other Dependencies

aiohttp: 3.13.3 anthropic: 0.84.0 bedrock-agentcore: 1.2.0 blockbuster: 1.5.26 boto3: 1.42.67 click: 8.3.1 cloudpickle: 3.1.2 croniter: 6.2.2 cryptography: 46.0.4 filetype: 1.2.0 google-genai: 1.60.0 grpcio: 1.78.0 grpcio-health-checking: 1.78.0 grpcio-tools: 1.78.0 httpx: 0.28.1 jsonpatch: 1.33 jsonschema-rs: 0.44.1 langgraph: 1.1.2 langgraph-checkpoint: 4.0.1 mcp: 1.26.0 numpy: 2.4.1 opentelemetry-api: 1.40.0 opentelemetry-exporter-otlp-proto-http: 1.40.0 opentelemetry-sdk: 1.40.0 orjson: 3.11.5 packaging: 25.0 protobuf: 6.33.5 pydantic: 2.12.5 pyjwt: 2.10.1 pytest: 9.0.2 python-dotenv: 1.2.1 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 rich: 14.3.3 sse-starlette: 3.2.0 starlette: 0.52.1 structlog: 25.5.0 tenacity: 9.1.2 truststore: 0.10.4 typing-extensions: 4.15.0 typing_extensions: 4.15.0 uuid-utils: 0.14.0 uvicorn: 0.40.0 watchfiles: 1.1.1 wcmatch: 10.1 zstandard: 0.25.0

PR fix notes

PR #35988: fix: change messages type from list to Sequence for type invariance

Description (problem / solution / changelog)

Fixes type invariance issue where list[BaseMessage] was rejected by ModelRequest.override() even though BaseMessage is a valid message type.

Changed messages: list[AnyMessage] to messages: Sequence[BaseMessage] in _ModelRequestOverrides and ModelRequest.

Sequence is covariant (read-only), which correctly models the input-only usage in override().

Fixes #35971

Changed files

  • libs/langchain_v1/langchain/agents/middleware/__init__.py (modified, +2/-0)
  • libs/langchain_v1/langchain/agents/middleware/types.py (modified, +3/-3)

PR #35995: fix(agents): change messages type from list[AnyMessage] to Sequence[BaseMessage]

Description (problem / solution / changelog)

Fixes #35971

ModelRequest.override(messages=...) rejected list[BaseMessage] due to Python's list invariance. Since list is invariant, list[BaseMessage] cannot be assigned to list[AnyMessage] even though BaseMessage is the parent type.

Changed messages type annotations from list[AnyMessage] to Sequence[BaseMessage] in three locations:

  • _ModelRequestOverrides.messages (TypedDict)
  • ModelRequest.messages (dataclass field)
  • ModelRequest.init.messages (parameter)

Sequence is covariant (read-only), so Sequence[BaseMessage] correctly accepts list[AIMessage], list[HumanMessage], list[BaseMessage], etc.

Changed files

  • libs/langchain_v1/langchain/agents/middleware/types.py (modified, +3/-3)

PR #35996: test(agents): add regression test for list[BaseMessage] typing in override()

Description (problem / solution / changelog)

Adds a test to verify that list[BaseMessage] can be passed to ModelRequest.override(messages=...) without type errors.

The fix was already in place—the codebase uses Sequence[BaseMessage] instead of list[AnyMessage] in both:

  • _ModelRequestOverrides.messages (line 71)
  • ModelRequest.messages (lines 80, 97, 110)

Using Sequence (which is covariant) instead of list (which is invariant) allows list[BaseMessage] to be passed where Sequence[BaseMessage] is expected.

This test verifies the exact pattern from issue #35971 works correctly.

Fixes #35971

Changed files

  • libs/langchain_v1/tests/unit_tests/agents/middleware/core/test_overrides.py (modified, +35/-0)

PR #36037: fix: Allow list[BaseMessage] in ModelRequest.override(messages=)

Description (problem / solution / changelog)

Summary

Change messages type from list[AnyMessage] to Sequence[BaseMessage] in _ModelRequestOverrides and ModelRequest to fix type invariance issue reported in #35971.

Problem

Since list is invariant in Python type system, passing list[BaseMessage] (common when filtering/transforming messages) failed type checking.

Solution

Using Sequence (covariant/read-only) correctly models the input-only usage in override().

Changes

  • _ModelRequestOverrides.messages: list[AnyMessage] -> Sequence[BaseMessage]
  • ModelRequest.messages: list[AnyMessage] -> Sequence[BaseMessage]

Fixes #35971

Changed files

  • libs/core/langchain_core/structured_query.py (modified, +1/-1)
  • libs/langchain_v1/langchain/agents/middleware/types.py (modified, +4/-4)

PR #36061: fix(agents): use Sequence[AnyMessage] in _ModelRequestOverrides to fix list invariance error

Description (problem / solution / changelog)

Summary

Fixes #35971

Python generics with list are invariant, so list[HumanMessage] is not assignable to list[AnyMessage] even though HumanMessage extends BaseMessage. This caused a type error when calling ModelRequest.override(messages=[HumanMessage(...)]).

Using Sequence[AnyMessage] resolves the issue because Sequence is covariant in its type parameter.

Changes

Changed messages: list[AnyMessage]messages: Sequence[AnyMessage] in _ModelRequestOverrides TypedDict.

Sequence was already imported from collections.abc in the module.

Before

request.override(messages=[HumanMessage(content="hi")])  # TypeError: list[HumanMessage] incompatible with list[AnyMessage]

After

request.override(messages=[HumanMessage(content="hi")])  # OK

Changed files

  • libs/langchain_v1/langchain/agents/middleware/types.py (modified, +1/-1)

Code Example

from langchain.agents.middleware import before_model, ModelRequest
from langchain_core.messages import BaseMessage, AIMessage, HumanMessage

@before_model
def trim_messages(request: ModelRequest) -> ModelRequest:
    # Filter messages — result is list[BaseMessage]
    trimmed: list[BaseMessage] = [
        m for m in request.messages if isinstance(m, (AIMessage, HumanMessage))
    ]
    # Pyright error: list[BaseMessage] is not assignable to list[AnyMessage]
    return request.override(messages=trimmed)

---

Argument of type "list[BaseMessage]" cannot be assigned to parameter "messages"
  of type "list[AIMessage | HumanMessage | ChatMessage | SystemMessage | FunctionMessage
  | ToolMessage | AIMessageChunk | ...]" in function "override"
  "list[BaseMessage]" is not assignable to "list[AIMessage | HumanMessage | ...]"
    Type parameter "_T@list" is invariant, but "BaseMessage" is not the same as
    "AIMessage | HumanMessage | ..." (reportArgumentType)
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

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

Related Issues / PRs

#35429 - same list invariance problem for _InputAgentState.messages #33732 - list invariance in AgentMiddleware state typing

Reproduction Steps / Example Code (Python)

from langchain.agents.middleware import before_model, ModelRequest
from langchain_core.messages import BaseMessage, AIMessage, HumanMessage

@before_model
def trim_messages(request: ModelRequest) -> ModelRequest:
    # Filter messages — result is list[BaseMessage]
    trimmed: list[BaseMessage] = [
        m for m in request.messages if isinstance(m, (AIMessage, HumanMessage))
    ]
    # Pyright error: list[BaseMessage] is not assignable to list[AnyMessage]
    return request.override(messages=trimmed)

Error Message and Stack Trace (if applicable)

Argument of type "list[BaseMessage]" cannot be assigned to parameter "messages"
  of type "list[AIMessage | HumanMessage | ChatMessage | SystemMessage | FunctionMessage
  | ToolMessage | AIMessageChunk | ...]" in function "override"
  "list[BaseMessage]" is not assignable to "list[AIMessage | HumanMessage | ...]"
    Type parameter "_T@list" is invariant, but "BaseMessage" is not the same as
    "AIMessage | HumanMessage | ..." (reportArgumentType)

Description

_ModelRequestOverrides.messages is typed as list[AnyMessage]. Since list is invariant in Python's type system, passing list[BaseMessage] (common when filtering/transforming messages) fails type checking even though every BaseMessage is a valid message.

A minimal fix would change messages: list[AnyMessage] to messages: Sequence[BaseMessage] in _ModelRequestOverrides. Sequence is covariant (read-only), which correctly models the input-only usage in override().

However, since override() passes values through to dataclasses.replace(), the ModelRequest dataclass field itself (messages: list[AnyMessage]) may also need to change to Sequence[BaseMessage] for full consistency — otherwise mypy flags the replace() call. The same pattern was already used for ModelResponse.result (line 282: result: list[BaseMessage]).

System Info

System Information

OS: Darwin OS Version: Darwin Kernel Version 24.6.0: Mon Jan 19 22:00:10 PST 2026; root:xnu-11417.140.69.708.3~1/RELEASE_X86_64 Python Version: 3.12.9 (v3.12.9:fdb81425a9a, Feb 4 2025, 12:21:36) [Clang 13.0.0 (clang-1300.0.29.30)]

Package Information

langchain_core: 1.2.19 langchain: 1.2.12 langsmith: 0.6.6 deepagents: 0.4.11 langchain_anthropic: 1.3.4 langchain_aws: 1.4.0 langchain_google_genai: 4.2.0 langchain_mcp_adapters: 0.2.1 langchain_tavily: 0.2.17 langgraph_api: 0.7.73 langgraph_checkpoint_aws: 1.0.6 langgraph_cli: 0.4.18 langgraph_runtime_inmem: 0.26.0 langgraph_sdk: 0.3.11

Optional packages not installed

deepagents-cli

Other Dependencies

aiohttp: 3.13.3 anthropic: 0.84.0 bedrock-agentcore: 1.2.0 blockbuster: 1.5.26 boto3: 1.42.67 click: 8.3.1 cloudpickle: 3.1.2 croniter: 6.2.2 cryptography: 46.0.4 filetype: 1.2.0 google-genai: 1.60.0 grpcio: 1.78.0 grpcio-health-checking: 1.78.0 grpcio-tools: 1.78.0 httpx: 0.28.1 jsonpatch: 1.33 jsonschema-rs: 0.44.1 langgraph: 1.1.2 langgraph-checkpoint: 4.0.1 mcp: 1.26.0 numpy: 2.4.1 opentelemetry-api: 1.40.0 opentelemetry-exporter-otlp-proto-http: 1.40.0 opentelemetry-sdk: 1.40.0 orjson: 3.11.5 packaging: 25.0 protobuf: 6.33.5 pydantic: 2.12.5 pyjwt: 2.10.1 pytest: 9.0.2 python-dotenv: 1.2.1 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 rich: 14.3.3 sse-starlette: 3.2.0 starlette: 0.52.1 structlog: 25.5.0 tenacity: 9.1.2 truststore: 0.10.4 typing-extensions: 4.15.0 typing_extensions: 4.15.0 uuid-utils: 0.14.0 uvicorn: 0.40.0 watchfiles: 1.1.1 wcmatch: 10.1 zstandard: 0.25.0

extent analysis

Fix Plan

To resolve the issue, we need to update the type hint for messages in _ModelRequestOverrides and potentially in the ModelRequest dataclass to use Sequence[BaseMessage] instead of list[AnyMessage]. This change will allow for covariance, which is suitable for read-only access to the messages.

Step-by-Step Solution:

  1. Update _ModelRequestOverrides: Change the type hint for messages to Sequence[BaseMessage].

from typing import Sequence

class _ModelRequestOverrides: # ... messages: Sequence[BaseMessage] # ...


2. **Update `ModelRequest` dataclass (if necessary)**:
   If mypy still flags the `replace()` call after updating `_ModelRequestOverrides`, you may need to update the `messages` field in the `ModelRequest` dataclass to also use `Sequence[BaseMessage]`.
   ```python
from dataclasses import dataclass
from typing import Sequence

@dataclass
class ModelRequest:
    # ...
    messages: Sequence[BaseMessage]
    # ...
  1. Verify Type Checking: After making these changes, re-run mypy to verify that the type checking errors related to the messages field are resolved.

Verification

To verify that the fix worked, you can:

  • Re-run the type checker (mypy) on your codebase to ensure the errors related to messages are resolved.
  • Test the functionality of ModelRequest and _ModelRequestOverrides to ensure that the changes did not introduce any runtime errors.

Extra Tips

  • When working with type hints, especially in the context of dataclasses and overrides, consider the variance of the types you're using. In this case, switching to Sequence (which is covariant) from list (which is invariant) resolves the issue.
  • Keep your type hints as specific as possible while allowing for the necessary flexibility. In this scenario, using Sequence[BaseMessage] instead of list[AnyMessage] provides a better model of the intended usage.

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

langchain - ✅(Solved) Fix ModelRequest.override(messages=...) rejects list[BaseMessage] due to list invariance [5 pull requests, 6 comments, 4 participants]