Skip to content

[Bug]: Plugin loader silently drops register* on missing required fields — should hard-fail or throw #72459

Description

@1fanwang

Bug type

Behavior bug — silent failure during plugin load.

Beta release blocker

No

Summary

The plugin loader silently drops register* calls that fail acceptance gates. The plugin still loads, the surface never fires, and the only signal is a level=warn diagnostic in gateway.log. Plugins ship green tests against helper-mock APIs and broken in production.

Steps to reproduce

In a plugin, register a hook without opts.name:

api.registerHook(
  ["before_tool_call", "after_tool_call"],
  (event, payload) => { /* ... */ }
);
// Missing: { name: "<plugin>-<purpose>", description: "..." }

Or call api.registerMemoryCapability(...) from a plugin whose manifest does not declare kind: "memory".

The api.registerX(...) call returns successfully. Helper-mock tests pass. The gateway loader logs:

[gateway] [plugins] hook registration missing name (plugin=<id>, source=...)
[gateway] [plugins] only memory plugins can register a memory capability (plugin=<id>, source=...)

…and silently drops the registration. The plugin loads with Status: loaded and the user has no in-band signal that anything is wrong.

Expected behavior

The loader should treat acceptance-gate failures as errors that either:

  1. Hard-fail the plugin loadStatus: error with the diagnostic surfaced via openclaw plugins doctor, or
  2. Throw synchronously from the api.registerX(...) call so the plugin's own error path runs (so unit tests can catch it via expect(...).toThrow)

Either is fine; the current "silent drop, log warn, plugin still loads" is the worst of both worlds — the plugin author has no defect signal until production. Helper-mock unit tests against a fake api cannot catch these gates because the mock has no gate logic.

Actual behavior

Plugin loads successfully. Surface never wires up. The only signal is a level=warn diagnostic in gateway.log that nothing in CI inspects.

Why this matters

Two real cases I hit while developing a plugin:

  • A before_tool_call/after_tool_call telemetry hook registered without opts.name. Helper test passed. Gateway dropped the hook. Telemetry was disabled in production for weeks before I noticed (caught while debugging an unrelated dashboard issue).
  • registerMemoryCapability(...) registered without declaring kind: "memory" in the manifest. Helper test passed. Gateway rejected the registration at every load. Memory artifacts were never indexed.

Both fixes are trivial once you know about them. The cost is the time-to-detect — which is open-ended without a hard-fail surface.

Proposed fix shape

In loader-*.js registerHook and the gated memory-* registrations, replace pushDiagnostic({ level: "warn"|"error", ... }) + early return with one of:

  • A throw from api.registerX(...) itself (preserves backwards-compat for plugins that already pass valid args; fails loud for plugins that don't)
  • A plugin-status downgrade (Status: error_partial or similar) that openclaw plugins inspect surfaces and CI can grep for

Workaround in use today

A doctor-check that scans gateway.log + gateway.err.log since the most recent gateway] ready event, counts plugin=<id> warnings, and fails CI if non-zero. Works but is per-plugin; an upstream fix would benefit every plugin author.

OpenClaw version

2026.4.24 (cbcfdf6). Source verified against installed /opt/homebrew/lib/node_modules/openclaw/dist/loader-NucjcOgv.js.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions