langchain-ai/langchain · error · ValueError

alternative_import must be a fully qualified module path. Go

Error message

alternative_import must be a fully qualified module path. Got  {alternative_import}

What it means

Raised by `_validate_deprecation_params` when `alternative_import` is supplied but contains no dot, i.e. it is not a fully qualified module path. The decorator uses the dotted path both to render 'import x.y.z' guidance in the warning and to surface the replacement object to tooling, so a bare name is rejected.

Source

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

    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 = "",
    alternative_import: str = "",
    pending: bool = False,
    obj_type: str = "",
    addendum: str = "",
    removal: str = "",
    package: str = "",
) -> Callable[[T], T]:
    """Decorator to mark a function, a class, or a property as deprecated.

    When deprecating a classmethod, a staticmethod, or a property, the `@deprecated`

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Use the fully qualified import path of the replacement: `alternative_import="langchain_x.new_module.new_fn"`.
  2. If no module path exists for the replacement, switch to the free-text `alternative=` kwarg instead (they are mutually exclusive anyway).

Example fix

# before
@deprecated("0.3", alternative_import="new_fn")

# after
@deprecated("0.3", alternative_import="mymod.submod.new_fn")
Defensive patterns

Strategy: validation

Validate before calling

import re

def is_qualified_path(path: str) -> bool:
    return bool(re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+", path))

assert is_qualified_path("mymod.new_fn") and not is_qualified_path("new_fn")

Prevention

When it happens

Trigger: `@deprecated(since="0.3", alternative_import="new_fn")` — a single identifier with no `.`. Note the rendered message contains a stray double space ('Got {alternative_import}') due to the f-string in the source; the underlying check is simply `'.' not in alternative_import`.

Common situations: Copy-pasting just the symbol name instead of its full path, or refactoring a module and updating the alternative to a name that lost its package prefix.

Related errors


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