langchain-ai/langchain · error · ValueError

A pending deprecation cannot have a scheduled removal

Error message

A pending deprecation cannot have a scheduled removal

What it means

Raised by `_validate_deprecation_params` in langchain-core's deprecation API when a decorator is declared `pending=True` while also passing a `removal` version. A pending deprecation is by definition not yet scheduled for removal, so specifying both is contradictory and the library rejects it at decoration time (import time for module/class/function decorators).

Source

Thrown at libs/core/langchain_core/_api/deprecation.py:107


# Bound is `Any` (not `FieldInfoV1`) because importing `pydantic.v1` at module
# scope emits a `UserWarning` on Python 3.14+; v1 `FieldInfo` support is handled
# at runtime via `_is_pydantic_v1_field_info`.
T = TypeVar("T", bound=type | Callable[..., Any] | Any)


def _validate_deprecation_params(
    removal: str,
    alternative: str,
    alternative_import: str,
    *,
    pending: bool,
) -> None:
    """Validate the deprecation parameters."""
    if pending and removal:
        msg = "A pending deprecation cannot have a scheduled removal"
        raise ValueError(msg)
    if alternative and alternative_import:
        msg = "Cannot specify both alternative and alternative_import"
        raise ValueError(msg)

    if alternative_import and "." not in alternative_import:
        msg = (
            "alternative_import must be a fully qualified module path. Got "
            f" {alternative_import}"
        )
        raise ValueError(msg)


def deprecated(
    since: str,
    *,
    message: str = "",
    name: str = "",
    alternative: str = "",

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Remove the `removal=` argument: `@deprecated(since="0.3", pending=True)` is the valid pending form.
  2. If you actually want a scheduled removal, drop `pending=True` instead: `@deprecated(since="0.3", removal="1.0")`.

Example fix

# before
@deprecated("0.3", pending=True, removal="1.0", alternative="new_fn")
def old_fn(): ...

# after
@deprecated("0.3", pending=True, alternative="new_fn")
def old_fn(): ...
Defensive patterns

Strategy: validation

Validate before calling

def validate_deprecation_kwargs(pending: bool, removal: str) -> None:
    if pending and removal:
        raise ConfigError("pending=True cannot be combined with removal=")

Prevention

When it happens

Trigger: Calling `@deprecated(since="0.3", pending=True, removal="1.0")` (or `warn_deprecated(..., pending=True, removal="1.0")`) on a class, function, method, or Pydantic field. The check fires before any decorated object is used, so the ValueError surfaces as soon as the module is imported.

Common situations: A developer copies an existing `@deprecated(...)` line that had `removal=` set, then adds `pending=True` to soften the warning. Also happens when promoting a hard deprecation to pending during a release-candidate refactor and forgetting to delete the `removal` kwarg.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/45c3db06c04dd654. Report an issue: GitHub.