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
- Change the factory declaration to `async def extension(...)`
- If the factory returns a callable object, wrap it: `async def extension(api): return Impl(api)`
- 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
- Declare the factory with `async def`, never `def` or `lambda`
- Run the extension loader against every extension in CI
- Grep for `def extension(` when migrating older extensions
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
- {source.path} does not define a callable 'extension' factory
- Extension factory in {source.path} attempted to exit: {exc}
- Extension factory in {source.path} failed: {exc}
- Extension registration is closed for this session
- Could not construct middleware {middleware!r}: {exc}
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/545ae198cb9d8b07.
Report an issue: GitHub.