{"record":{"id":"5f2444fd50277c2a","repo":"langchain-ai/deepagents","slug":"source-path-does-not-define-a-callable-extensio","errorCode":null,"errorMessage":"{source.path} does not define a callable 'extension' factory","messagePattern":"(.+?) does not define a callable 'extension' factory","errorType":"validation","errorClass":"ExtensionError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/extensions/loader.py","lineNumber":59,"sourceCode":"    module = importlib.util.module_from_spec(spec)\n    sys.modules[name] = module\n    try:\n        spec.loader.exec_module(module)\n    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.","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/extensions/loader.py#L41-L77","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\n# my_ext.py\ndef setup(api):\n    ...\n\n// after\n# my_ext.py\nimport inspect\n\nasync def extension(api):\n    ...","handlingStrategy":"validation","validationCode":"import inspect, importlib.util\nspec = importlib.util.spec_from_file_location(\"my_ext\", path)\nmod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)\nif not callable(getattr(mod, \"extension\", None)):\n    raise SystemExit(f\"{path}: no callable 'extension' factory\")","typeGuard":"def has_extension_factory(mod) -> bool:\n    return callable(getattr(mod, \"extension\", None))","tryCatchPattern":"try:\n    name, factory = _import_factory(source)\nexcept ExtensionError as exc:\n    logger.error(\"bad extension %s: %s\", source.path, exc)","preventionTips":["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"],"tags":["extensions","plugin-loading","python"],"backgroundTag":"missing-plugin-entry-point","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}