Skip to content

core-plugins entry_point (plugins.core_plugins.*) not importable — plugin-loader tool registration silently fails #10294

Closed

Description

@mrveiss

Summary

The plugin loader (autobot_shared/plugin_sdk/loader.py:275) registers plugins via importlib.import_module(manifest.entry_point). Core plugins live under plugins/core-plugins/<name>/ (hyphenated dir) but declare entry_point: "plugins.core_plugins.<name>.main" (underscored module path). There is no path hook, namespace package, __init__.py, or dir alias that maps the hyphenated directory to the underscored importable module, so the import fails.

Evidence

python3 -c "import importlib; importlib.import_module('plugins.core_plugins.image_generation_plugin.main')"
# ModuleNotFoundError: No module named 'plugins.core_plugins'

Impact

  • The image-generation-plugin (GH#9015) and the new video-generation-plugin (GH#9016) both fail to load through the plugin loader, so their initialize() never registers generate_image / generate_video into the tool_sdk registry via the loader path.
  • The REST endpoints still work (they import the provider/tool logic directly via file path), but the intended LLC-agent tool_sdk registry registration relies on the loader.

Suggested fix (wire it in, don't delete)

Add a plugin-path resolver in PluginLoader._import_plugin_class that, when import_module fails, falls back to importlib.util.spec_from_file_location using the manifest's on-disk directory (mapping core_plugins <-> core-plugins and <name>_plugin <-> <name>-plugin), registering the module in sys.modules so dataclasses resolve. The video-generation API already does this for its providers (autobot-backend/api/_video_providers_loader.py) and can serve as the reference.

Found while implementing GH#9016.

Activity

  1. mrveiss commented on Jun 22, 2026

    @mrveiss
    OwnerAuthor

    Deploy half fixed in #10447 (merged): backend role now syncs code_source/plugins/ → backend_code_dir/plugins/, so discover_plugins() finds the core plugins (the Plugin directory does not exist flood is gone — validated on the box). The import half (file-path fallback for hyphenated dirs) lands via #10442. This issue fully resolves once #10442 merges.

  2. github-actions commented on Jun 22, 2026

    @github-actions
    Contributor

    PR #10442 (merged to Dev_new_gui) references this issue with a close keyword.

    feat(integrations): plugin-loader file-path fallback for core-plugins + route WhatsApp inbound media into chat (#10294, #10267)

    If this issue is fully resolved, close it manually. If work remains, no action is needed.

  3. mrveiss commented on Jun 22, 2026

    @mrveiss
    OwnerAuthor

    Merged to Dev_new_gui (4d5f464bd). Plugin-loader file-path fallback for core-plugins (hyphen-dir → underscore module) merged; loader now resolves image/video-generation plugins via spec_from_file_location with Plugin alias + BasePlugin fallback. Batch #10440.

  4. reopened this on Aug 7, 2026
  5. mrveiss commented on Aug 7, 2026

    @mrveiss
    OwnerAuthor

    Reopening — the file-path fallback added for this issue does not work. Two core plugins still fail to
    import on every backend start, observed on 84b88baa1591 after a clean host boot.

    Current failure

    ERROR autobot_shared.plugin_sdk.loader: Failed to import plugin module:
          plugins.core_plugins.image_generation_plugin.main
    WARN  File-path fallback failed for 'plugins.core_plugins.image_generation_plugin.main'
          at plugins/core-plugins/image-generation-plugin/main.py: No module named 'plugins.core_plugins'
    
    ERROR autobot_shared.plugin_sdk.loader: Failed to import plugin module:
          plugins.core_plugins.video_generation_plugin.main
    WARN  File-path fallback failed for 'plugins.core_plugins.video_generation_plugin.main'
          at plugins/core-plugins/video-generation-plugin/main.py: No module named 'plugins.core_plugins'
    

    The fallback is present and attributed to this issue:

    # autobot_shared/plugin_sdk/loader.py:205
    # Import plugin module — pass on-disk directory for file-path fallback (#10294)
    plugin_dir = self._manifest_dirs.get(manifest.name)
    plugin_class = self._import_plugin_class(manifest.entry_point, plugin_dir)

    It locates the correct file — the warning names plugins/core-plugins/image-generation-plugin/main.py,
    which exists — and then still raises No module named 'plugins.core_plugins'. So the fallback resolves the
    path and then imports by dotted name anyway, which is the exact failure it was meant to bypass.

    The underlying mismatch is unchanged

    entry_point uses underscores; the directories use hyphens:

    entry_point   plugins.core_plugins.image_generation_plugin.main
    on disk       plugins/core-plugins/image-generation-plugin/
    

    Confirmed layout:

    plugins/core-plugins/hello-plugin/
    plugins/core-plugins/image-generation-plugin/
    plugins/core-plugins/kb-event-plugin/
    plugins/core-plugins/logger-plugin/
    plugins/core-plugins/mcp-wrapper-plugin/
    plugins/core-plugins/telemetry-prompt-middleware/
    plugins/core-plugins/video-generation-plugin/
    

    Impact

    Image generation and video generation are both unavailable, and the failure is quiet — the backend starts
    active and healthy. The log also shows these two plugins reporting loaded with optional env vars unset: ['OPENAI_API_KEY', 'FLUX_API_KEY', 'STABILITY_API_KEY'] shortly before the import fails, so a reader
    skimming the log sees "loaded" and an unrelated-looking import error separately.

    Suggested fix

    Have the fallback load from the located file — importlib.util.spec_from_file_location with a synthetic
    module name — rather than re-attempting a dotted import. A test that loads every manifest under
    plugins/core-plugins/ would cover all seven and stop this reopening a third time.

    Related: the same loader has two further defects filed separately (dependency check consulting the wrong
    registry; discovery running twice).

  6. mrveiss commented on Aug 9, 2026

    @mrveiss
    OwnerAuthor

    Part of umbrella #13852

    Wave 2 — signal exists, nothing consumes it

    Update #13852 when this issue changes state

    This issue appears as a checklist item on #13852. Whoever moves it is responsible for the
    umbrella staying accurate — the umbrella is the only place the ordering and the shared acceptance
    criterion live, so a stale checklist there silently breaks the sequencing for everything else.

    Why these are grouped

    Eleven findings from one diagnostic session sharing a single property: the system was failing and
    nothing said so — or something said the opposite. The umbrella's acceptance criterion is the part
    no individual child owns:

    A service that is running but not working must be distinguishable from one that is working, by
    something a check can query — not by reading logs.

    systemctl is-active returned active through four separate incidents in that session.

    #12460/#13736 is linked on the umbrella as the worked example to copy — metric plus alert rule,
    verified end-to-end against the worker's own log for the same synthesis.

  7. mrveiss commented on Aug 10, 2026

    @mrveiss
    OwnerAuthor

    Reproduced on Dev_new_gui: 7 discovered, 0 loaded

    Driving the real PluginLoader against the real plugins/ tree:

    loaded=0 failed=7
    

    So #13677's headline holds exactly. But it is three distinct causes, not one, and only two of them are
    what the issues currently describe. The one that affects the most plugins is not mentioned anywhere.

    Route 1 — dual-identity BasePlugin (4 of 7: logger, mcp-wrapper, hello, kb-event)

    ERROR: No plugin class found in module: plugins.core_plugins.hello_plugin.main
    

    The module imports fine. The class is there. The loader simply cannot recognise it:

    plugins/core-plugins/hello-plugin/main.py:15
        from plugin_sdk.base import BasePlugin      <- bare `plugin_sdk`
    
    autobot_shared/plugin_sdk/loader.py
        issubclass(attr, BasePlugin)                <- autobot_shared.plugin_sdk.base.BasePlugin
    

    Verified they are different class objects:

    canonical: autobot_shared.plugin_sdk.base
    alt      : plugin_sdk.base
    SAME CLASS OBJECT: False
    

    issubclass is therefore False and the loader reports "No plugin class found" — an error message that sends
    the reader looking for a missing class that is right there. This is the single largest route and it is
    described in neither #10294 nor #13677.

    Route 2 — the file-path fallback loads the file, then its own imports fail (2 of 7)

    This is #10294's remaining bug, and the umbrella's phrasing is nearly right:

    WARNING: File-path fallback failed for 'plugins.core_plugins.image_generation_plugin.main'
             at plugins/core-plugins/image-generation-plugin/main.py: No module named 'plugins.core_plugins'
    

    The fallback does locate the file and does use spec_from_file_location — that part of #10294 is
    already implemented. What fails is exec_module: the plugin's own transitive imports reference
    plugins.core_plugins.*, and those parent packages exist nowhere in sys.modules. So the fix is not "load
    from the located file" (already done) but "make the package path resolvable for the module's own imports" —
    register synthetic namespace packages for plugins, plugins.core_plugins, and the plugin dir before
    exec_module.

    Note the fallback also hardcodes main.py, while telemetry-prompt-middleware declares
    entry_point: ...telemetry_prompt_middleware.plugin and ships plugin.py. The last path segment should drive
    the filename.

    Route 3 — a missing optional dependency (1 of 7)

    ERROR: Cannot load plugin telemetry-prompt-middleware: missing dependencies ['aiohttp']
    

    Relates to #13677's "dependency check resolves pip packages against the plugin registry".

    Why this matches the umbrella

    Every route fails silently at the fleet level: nothing emits loaded N of M, so 0-of-7 and
    "no plugins installed" are the same observation. Route 1 is worse than silent — it emits a confident error
    naming the wrong thing.

    Suggested split

  8. github-actions commented on Aug 10, 2026

    @github-actions
    Contributor

    PR #13944 (merged to Dev_new_gui) references this issue with a close keyword.

    fix(plugin-sdk): file-path fallback located the plugin file and could not execute it (#10294)

    If this issue is fully resolved, close it manually. If work remains, no action is needed.

  9. mrveiss commented on Aug 10, 2026

    @mrveiss
    OwnerAuthor

    PR #13944 merged — commit 79b526b62, verified in Dev_new_gui.

    The suggested fix was already implemented: _import_from_file does use spec_from_file_location and does
    locate the correct file. What failed was exec_module — the plugin's own imports reference
    plugins.core_plugins.*, and those parent packages exist in no sys.modules. The log read "File-path
    fallback failed ... No module named 'plugins.core_plugins'", i.e. the loader reporting it could not load a
    file it had already found, which is why the fallback looked broken rather than incomplete.

    Each parent now gets a namespace module whose __path__ points at the matching real directory, walked up
    from the module file
    so the hyphen/underscore mapping is derived rather than guessed. An existing real
    plugins package is never replaced, and a failed exec withdraws what it created.

    Also: the fallback hardcoded main.py, so telemetry-prompt-middleware — which declares
    ...telemetry_prompt_middleware.plugin and ships plugin.py — could never have been found by it.

    Measured effect. This alone changed no plugin from failing to loading; it moved two from route 2's error
    to route 1's, which is what proved it worked. Combined with #13677 (PR #13952) on the real tree:

    before:  loaded=0 failed=7
    after:   loaded 5 of 7 — failed: logger-plugin, telemetry-prompt-middleware
    

    Closing this one: its specific defect is fixed and verified end-to-end against the shipped tree. The remaining
    two failures are separate defects tracked on #13677.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions