{"record":{"id":"545ae198cb9d8b07","repo":"langchain-ai/deepagents","slug":"extension-factory-in-source-path-must-be-declare","errorCode":null,"errorMessage":"Extension factory in {source.path} must be declared with 'async def'","messagePattern":"Extension factory in (.+?) must be declared with 'async def'","errorType":"validation","errorClass":"ExtensionError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/extensions/loader.py","lineNumber":63,"sourceCode":"    except (KeyboardInterrupt, SystemExit, Exception) as exc:\n        sys.modules.pop(name, None)\n        if isinstance(exc, KeyboardInterrupt):\n            raise\n        msg = (\n            f\"Extension import in {source.path} attempted to exit: {exc}\"\n            if isinstance(exc, SystemExit)\n            else f\"Failed to import {source.path}: {exc}\"\n        )\n        raise ExtensionError(msg) from exc\n    factory = getattr(module, \"extension\", None)\n    if not callable(factory):\n        sys.modules.pop(name, None)\n        msg = f\"{source.path} does not define a callable 'extension' factory\"\n        raise ExtensionError(msg)\n    if not inspect.iscoroutinefunction(factory):\n        sys.modules.pop(name, None)\n        msg = f\"Extension factory in {source.path} must be declared with 'async def'\"\n        raise ExtensionError(msg)\n    return name, factory\n\n\nasync def load_extension(\n    source: SourceInfo,\n    registry: ExtensionRegistry,\n    *,\n    cwd: Path,\n    mode: ExtensionMode,\n) -> ExtensionAPI:\n    \"\"\"Load one extension transactionally.\n\n    Args:\n        source: Extension entry file and import shape.\n        registry: Destination for registrations.\n        cwd: Session working directory.\n        mode: Runtime mode.\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/extensions/loader.py#L45-L81","documentation":"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.","triggerScenarios":"An extension module defines `def extension(api): ...` (or a `lambda`, which is never a coroutine function) instead of `async def extension(api): ...`.","commonSituations":"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.","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"],"exampleFix":"// before\ndef extension(api):\n    api.register_command(\"hello\", hello_cmd)\n    return api\n\n// after\nasync def extension(api):\n    api.register_command(\"hello\", hello_cmd)\n    return api","handlingStrategy":"validation","validationCode":"import inspect\nif not inspect.iscoroutinefunction(getattr(ext_module, \"extension\", None)):\n    raise TypeError(\"extension must be 'async def'\")","typeGuard":"import inspect\ndef is_async_factory(obj) -> bool:\n    return callable(obj) and inspect.iscoroutinefunction(obj)","tryCatchPattern":"try:\n    name, factory = _import_factory(source)\nexcept ExtensionError as exc:\n    logger.error(\"%s: %s\", source.path, exc)","preventionTips":["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"],"tags":["extensions","async","plugin-loading"],"backgroundTag":"sync-factory-where-async-required","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}