langchain-ai/deepagents · error · ExtensionError

Extension factory in {source.path} must be declared with 'as

Error message

Extension factory in {source.path} must be declared with 'async def'

What it means

Raised by `_import_factory` when the module's `extension` factory exists and is callable but is a plain (synchronous) function. The extension system requires factories to be coroutine functions (`async def`) so loading can perform async setup. The module is popped from `sys.modules` and an `ExtensionError` with the file path is raised.

Source

Thrown at libs/code/deepagents_code/extensions/loader.py:63

    except (KeyboardInterrupt, SystemExit, Exception) as exc:
        sys.modules.pop(name, None)
        if isinstance(exc, KeyboardInterrupt):
            raise
        msg = (
            f"Extension import in {source.path} attempted to exit: {exc}"
            if isinstance(exc, SystemExit)
            else f"Failed to import {source.path}: {exc}"
        )
        raise ExtensionError(msg) from exc
    factory = getattr(module, "extension", None)
    if not callable(factory):
        sys.modules.pop(name, None)
        msg = f"{source.path} does not define a callable 'extension' factory"
        raise ExtensionError(msg)
    if not inspect.iscoroutinefunction(factory):
        sys.modules.pop(name, None)
        msg = f"Extension factory in {source.path} must be declared with 'async def'"
        raise ExtensionError(msg)
    return name, factory


async def load_extension(
    source: SourceInfo,
    registry: ExtensionRegistry,
    *,
    cwd: Path,
    mode: ExtensionMode,
) -> ExtensionAPI:
    """Load one extension transactionally.

    Args:
        source: Extension entry file and import shape.
        registry: Destination for registrations.
        cwd: Session working directory.
        mode: Runtime mode.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Change the factory declaration to `async def extension(...)`
  2. If the factory returns a callable object, wrap it: `async def extension(api): return Impl(api)`
  3. Move any synchronous setup inside the async factory body

Example fix

// before
def extension(api):
    api.register_command("hello", hello_cmd)
    return api

// after
async def extension(api):
    api.register_command("hello", hello_cmd)
    return api
Defensive patterns

Strategy: validation

Validate before calling

import inspect
if not inspect.iscoroutinefunction(getattr(ext_module, "extension", None)):
    raise TypeError("extension must be 'async def'")

Type guard

import inspect
def is_async_factory(obj) -> bool:
    return callable(obj) and inspect.iscoroutinefunction(obj)

Try / catch

try:
    name, factory = _import_factory(source)
except ExtensionError as exc:
    logger.error("%s: %s", source.path, exc)

Prevention

When it happens

Trigger: An extension module defines `def extension(api): ...` (or a `lambda`, which is never a coroutine function) instead of `async def extension(api): ...`.

Common situations: Porting a sync plugin from another framework; older extension examples written before the loader mandated async factories; converting a class's `__call__` which is sync even when other methods are async.

Related errors


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