langchain-ai/deepagents · error · ExtensionError
{source.path} does not define a callable 'extension' factory
Error message
{source.path} does not define a callable 'extension' factory What it means
Raised by `_import_factory` in the extension loader when a dynamically imported extension module does not expose a module-level `extension` callable. The loader expects every extension source file to define an `extension` async factory that the host calls to obtain the extension API. If the attribute is absent or not callable, the module is evicted from `sys.modules` and an `ExtensionError` is raised pointing at the offending file path.
Source
Thrown at libs/code/deepagents_code/extensions/loader.py:59
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
try:
spec.loader.exec_module(module)
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.View on GitHub (pinned to a1af029e6e)
Solutions
- Define a module-level async factory named exactly `extension` in the extension file
- If the factory exists, check for a typo or shadowing of the name `extension` in the module
- Verify the file is an actual dcode extension and not a generic Python script placed in the extensions directory
Example fix
// before
# my_ext.py
def setup(api):
...
// after
# my_ext.py
import inspect
async def extension(api):
... Defensive patterns
Strategy: validation
Validate before calling
import inspect, importlib.util
spec = importlib.util.spec_from_file_location("my_ext", path)
mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)
if not callable(getattr(mod, "extension", None)):
raise SystemExit(f"{path}: no callable 'extension' factory") Type guard
def has_extension_factory(mod) -> bool:
return callable(getattr(mod, "extension", None)) Try / catch
try:
name, factory = _import_factory(source)
except ExtensionError as exc:
logger.error("bad extension %s: %s", source.path, exc) Prevention
- Always define `async def extension(api): ...` as the module entry point
- Start from the current extension template/example
- Smoke-test extensions by loading them in a test before shipping
When it happens
Trigger: Importing an extension file (via `load_extension`/`_import_factory`) whose module defines no `extension` attribute, or defines `extension` as a non-callable value (e.g. a class instance, string, or constant).
Common situations: Hand-written extension files copied from outdated templates that named the factory differently (e.g. `main`, `setup`, `create_extension`); typos like `extention`; extensions written for another plugin system with a different entry-point convention.
Related errors
- Extension factory in {source.path} must be declared with 'as
- Extension factory in {source.path} attempted to exit: {exc}
- Extension factory in {source.path} failed: {exc}
- modes can only be provided when agent is a factory
- models can only be provided when agent is a factory
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/5f2444fd50277c2a.
Report an issue: GitHub.