[PR #6427] fix _strip_extras helper function #5034

Open
opened 2026-02-20 17:51:07 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/langchain-ai/langgraph/pull/6427

State: open
Merged: No


Hello everyone !
While reading the code of libs/langgraph/langgraph/channels/binop.py, I found out that the helper function _strip_extras may have a small typo:

# Adapted from typing_extensions
def _strip_extras(t):  # type: ignore[no-untyped-def]
    """Strips Annotated, Required and NotRequired from a given type."""
    if hasattr(t, "__origin__"):
        return _strip_extras(t.__origin__)
    if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
        return _strip_extras(t.__args__[0])

    return t

There is an issue because the second part of the function will never be reached because if hasattr(t, "__origin__") == False then hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired) == False too.

The original implementation from typing_extension is github link :

  def _strip_extras(t):
      """Strips Annotated, Required and NotRequired from a given type."""
      if isinstance(t, typing._AnnotatedAlias):
          return _strip_extras(t.__origin__)
      if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly):
          return _strip_extras(t.__args__[0])
      # Other cases...

We can see that they don't use hasattr(t, "__origin__") as first condition but isinstance(t, typing._AnnotatedAlias) which specifically targets annotations.

This code was introduced in this commit. According to the docstring of the function, the first two lines are supposed to target the annotations specifically but I am not 100% certain that changing if hasattr(t, "__origin__"): to if isinstance(t, typing._AnnotatedAlias): will not break other parts of the code (moreover there might be a reason the author of this commit made that choice.).
But switching the two ifs seams to fix the function behaviour without breaking any existing code, this is why I propose this small fix.

Sample code for the bug

We can exhibit the bug when manipulating the BinaryOperatorAggregate object:

from typing import Required
import operator
from langgraph.channels.binop import BinaryOperatorAggregate

agg = BinaryOperatorAggregate(Required[list[int]], operator.add)
agg.value.append(1)

returns
AttributeError: 'object' object has no attribute 'append'

But

from typing import Required
import operator
from langgraph.channels.binop import BinaryOperatorAggregate

agg = BinaryOperatorAggregate(list[int], operator.add)
agg.value.append(1)

works fine.

Higher level object manipulation

It looks like this code has no impact over higher level objects because the BinaryOperatorAggregate values are always updated via the update() method that will handle MISSING flag. the BinaryOperatorAggregate(...).value is never assigned directly in the code.

**Original Pull Request:** https://github.com/langchain-ai/langgraph/pull/6427 **State:** open **Merged:** No --- Hello everyone ! While reading the code of `libs/langgraph/langgraph/channels/binop.py`, I found out that the helper function `_strip_extras` may have a small typo: ```python # Adapted from typing_extensions def _strip_extras(t): # type: ignore[no-untyped-def] """Strips Annotated, Required and NotRequired from a given type.""" if hasattr(t, "__origin__"): return _strip_extras(t.__origin__) if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired): return _strip_extras(t.__args__[0]) return t ``` There is an issue because the second part of the function will never be reached because if `hasattr(t, "__origin__") == False` then `hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired) == False` too. The original implementation from typing_extension is [github link](https://github.com/python/typing_extensions/blob/196082138aebe36af7f5a1d08691b84dab950eea/src/typing_extensions.py#L1470C23-L1470C26) : ```python def _strip_extras(t): """Strips Annotated, Required and NotRequired from a given type.""" if isinstance(t, typing._AnnotatedAlias): return _strip_extras(t.__origin__) if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly): return _strip_extras(t.__args__[0]) # Other cases... ``` We can see that they don't use `hasattr(t, "__origin__")` as first condition but `isinstance(t, typing._AnnotatedAlias)` which specifically targets annotations. This code was introduced in [this commit](https://github.com/langchain-ai/langgraph/commit/cec6ba4963d8746c577c3e68d395ef23bc725c4f). According to the docstring of the function, the first two lines are supposed to target the annotations specifically but I am not 100% certain that changing `if hasattr(t, "__origin__"):` to `if isinstance(t, typing._AnnotatedAlias):` will not break other parts of the code (moreover there might be a reason the author of this commit made that choice.). But switching the two ifs seams to fix the function behaviour without breaking any existing code, this is why I propose this small fix. #### Sample code for the bug We can exhibit the bug when manipulating the `BinaryOperatorAggregate` object: ```python from typing import Required import operator from langgraph.channels.binop import BinaryOperatorAggregate agg = BinaryOperatorAggregate(Required[list[int]], operator.add) agg.value.append(1) ``` returns `AttributeError: 'object' object has no attribute 'append'` But ```python from typing import Required import operator from langgraph.channels.binop import BinaryOperatorAggregate agg = BinaryOperatorAggregate(list[int], operator.add) agg.value.append(1) ``` works fine. #### Higher level object manipulation It looks like this code has no impact over higher level objects because the `BinaryOperatorAggregate` values are always updated via the `update()` method that will handle `MISSING` flag. the `BinaryOperatorAggregate(...).value` is never assigned directly in the code.
yindo added the pull-request label 2026-02-20 17:51:07 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#5034