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
- Remove the `removal=` argument: `@deprecated(since="0.3", pending=True)` is the valid pending form.
- 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
- Keep a single lint rule (ruff custom check or grep in CI) over deprecation decorators asserting not (pending and removal).
- Use keyword args consistently in @deprecated calls so an extra removal= kwarg is visible in review.
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
- Cannot specify both alternative and alternative_import
- alternative_import must be a fully qualified module path. Go
- Field {obj} must have a name to be deprecated.
- {f.__name__}() got multiple values for argument {new!r}
- module '{package!r}' has no attribute {attr_name!r}
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/45c3db06c04dd654.
Report an issue: GitHub.