langchain-ai/deepagents · error · ExtraNotInstalledError

Extra {extra!r} is not installed. Selected extras: {listed}

Error message

Extra {extra!r} is not installed. Selected extras: {listed}

What it means

Raised when the extra to remove is not among the currently selected/installed uv tool extras. The library checks membership in the selected set after canonicalizing the name and reports the full list of what is installed to help correct the input.

Source

Thrown at libs/code/deepagents_code/update_check.py:3801

            f"Invalid extra name {extra!r}: must match PEP 508 "
            f"({_EXTRA_NAME_RE.pattern})"
        )
        raise ValueError(msg)
    from deepagents_code.extras_info import BASE_DEPENDENCY_EXTRAS

    selected_extra = canonicalize_name(extra)
    if selected_extra in BASE_DEPENDENCY_EXTRAS:
        msg = f"Extra {extra!r} is a base dependency and cannot be removed."
        raise ProtectedExtraError(msg)

    extras = _uv_tool_selected_extras(distribution_name=distribution_name)
    conflict = _composite_extra_conflict_message(extra, selected=extras)
    if conflict is not None:
        raise CompositeExtraConflictError(conflict)
    if selected_extra not in extras:
        listed = ", ".join(sorted(extras)) or "(none)"
        msg = f"Extra {extra!r} is not installed. Selected extras: {listed}"
        raise ExtraNotInstalledError(msg)
    return _uv_tool_install_command(
        version=version,
        include_prereleases=_resolve_include_prereleases(
            None, installed=_parse_version(version)
        ),
        distribution_name=distribution_name,
        extras_to_remove=(selected_extra,),
        reinstall=True,
    )


def _composite_extra_conflict_message(
    extra: str,
    *,
    selected: Collection[str],
) -> str | None:
    """Return why a selected composite prevents removing `extra`, if any.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Check the selected extras listed in the message and use an exact installed name.
  2. Install the extra first if you actually intended to add it, not remove it.
  3. Canonicalize/normalize the extra name (underscores/hyphens/case) before calling.

Example fix

// before
remove_extra('browser_tool')  # not installed
// after
remove_extra('browser-tools')
Defensive patterns

Strategy: validation

Validate before calling

from deepagents_code.update_check import _uv_tool_selected_extras
from packaging.utils import canonicalize_name
selected = {canonicalize_name(e) for e in _uv_tool_selected_extras(distribution_name=dist)}
if canonicalize_name(name) not in selected:
    raise KeyError(f'{name} is not installed; selected: {sorted(selected)}')

Try / catch

try:
    remove_extra(name)
except ExtraNotInstalledError as e:
    logger.info('nothing to do: %s', e)  # idempotent removal

Prevention

When it happens

Trigger: Calling the remove-extra API (update_check.py) with an extra whose canonicalized name is absent from the set returned by _uv_tool_selected_extras; also when no extras are installed at all (message shows '(none)').

Common situations: Typo in the extra name or wrong casing; the extra was already removed; assuming an extra exists when the tool was installed without it; switching between distributions/versions with different extra sets.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/d56ebca005892c7f. Report an issue: GitHub.