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
- Use the fully qualified import path of the replacement: `alternative_import="langchain_x.new_module.new_fn"`.
- 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
- Copy alternative_import from an actual working import statement, then delete the 'from ... import ' wrapper text.
- CI smoke-import of deprecated modules catches malformed paths immediately.
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
- A pending deprecation cannot have a scheduled removal
- Cannot specify both alternative and alternative_import
- 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/d8f1e98aa362995c.
Report an issue: GitHub.