langchain-ai/deepagents · error · ExtensionError

Extension registration is closed for this session

Error message

Extension registration is closed for this session

What it means

Each extension gets an `ExtensionAPI` registrar that is active only during its factory run. After initialization fails or the session shuts down, `_deactivate()` closes the registrar, and every registration method (`register_middleware`, `register_tool`, `register_backend_route`, `on_shutdown`) raises `ExtensionError` via `_ensure_active`. This prevents extensions from mutating a registry that is no longer live.

Source

Thrown at libs/code/deepagents_code/extensions/api.py:68

            registry: Shared registry receiving extension units.
            source: Provenance recorded on each registration.
            cwd: Working directory for this session.
            mode: Runtime mode, either `interactive` or `headless`.
        """
        self._registry = registry
        self._source = source
        self._cwd = cwd
        self._mode = ExtensionMode(mode)
        self._active = True

    def _deactivate(self) -> None:
        """Close this registrar after failed initialization or shutdown."""
        self._active = False

    def _ensure_active(self) -> None:
        if not self._active:
            msg = "Extension registration is closed for this session"
            raise ExtensionError(msg)

    @property
    def cwd(self) -> Path:
        """Working directory for this session."""
        return self._cwd

    @property
    def mode(self) -> ExtensionMode:
        """Runtime mode, either `interactive` or `headless`."""
        return self._mode

    @property
    def has_ui(self) -> bool:
        """Whether this session has an interactive terminal UI."""
        return self._mode == ExtensionMode.INTERACTIVE

    @property
    def path(self) -> Path:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Perform all registrations synchronously inside the extension factory before returning.
  2. Use `api.on_shutdown(...)` inside the factory to schedule post-shutdown work instead of registering later.
  3. Guard deferred registration attempts with a check for session liveness, or capture desired registrations and apply them in the factory.
  4. If initialization failed, fix the underlying init error and restart the session — the registrar cannot be reopened.

Example fix

# before
threading.Timer(5.0, lambda: api.register_tool(tool)).start()

# after
api.register_tool(tool)  # register before the factory returns
Defensive patterns

Strategy: try-catch

Validate before calling

# inside the extension factory, register everything before returning
def activate(api):
    api.register_tool(my_tool)
    api.register_middleware(MyMiddleware())
    api.on_shutdown(cleanup)
    # do NOT stash `api` for later use

Type guard

def can_register(api) -> bool:
    return getattr(api, "_active", False)

Try / catch

try:
    api.register_tool(tool)
except ExtensionError as exc:
    if "registration is closed" in str(exc):
        logger.error("session ended; tool %s was never registered", tool.name)
    else:
        raise

Prevention

When it happens

Trigger: Calling any `register_*` method or `on_shutdown` from a background thread/async task after the factory returned; keeping a reference to the registrar and registering during a later shutdown hook; reusing a cached registrar instance across sessions.

Common situations: Extensions that spawn worker threads which lazily register tools on first use; extensions registering in `atexit`-style cleanup; retry logic that re-invokes the factory's returned registrar after a failed init.

Related errors


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