Skip to content

[Bug]: openclaw message send CLI loads the full plugin registry on every call #73006

@jasonftl

Description

@jasonftl

Bug type

Bug (CLI scope defaults to "all", loads every plugin manifest just to send one message)

Beta release blocker

No

Summary

Sending a message via openclaw message send ends up loading every plugin shipped with openclaw before doing the send. With ~88 bundled plugins on this install that is 2-3 minutes of CPU on a Pi 500, even when you just want to deliver one Discord message. The command only needs the channel it is sending to.

Steps to reproduce

  1. Run openclaw 2026.4.25
  2. Time a single CLI message send:
time openclaw message send --channel discord --target <id> --dry-run -m "x"

Expected behaviour

A CLI invocation that knows its target channel ahead of time should load only that channel's plugin (or at most the configured channels), not every plugin in the install.

Actual behaviour

The function that handles message-send (runMessageAction in dist/register.message-*.js) calls the registry loader without telling it which plugin it actually needs. The loader's default behaviour is to scan every plugin directory under dist/extensions/, open each openclaw.plugin.json, and parse the contents. The send itself only needs the channel plugin's manifest.

The loader does support narrower modes - "only configured channels", "only one specific plugin id" - but the message-send code path does not pass any of them, so it always gets the full scan.

OpenClaw version

2026.4.25

Environment

Raspberry Pi 500 (Debian Trixie, aarch64). Gateway runs as a systemd user service. Many side-of-house scripts use openclaw message send (boot-check posts two reports per gateway restart, cron jobs deliver to Discord).

Logs, screenshots, and evidence

Standalone timing on a healthy gateway:

time openclaw message send --channel discord --target <id> --dry-run -m "x"
Scope passed Wall time
(default "all") ~2m30s
{ scope: "configured-channels" } (2 channels here) 1m9s
{ onlyPluginIds: [opts.channel] } 10.7s

CPU is almost entirely on the V8 main thread. V8 --prof capture during a separate gateway boot showed ~52% of all JS CPU time inside node_modules/json5/lib/parse.js - that is the manifest parse path, hit once per plugin during the registry load. So the cost is real per-call work, not stale gateway state.

Code pointers on this install:

  • dist/register.message-CaRx3OC0.js line 113 - runMessageAction calls ensurePluginRegistryLoaded() with no args
  • dist/runtime-registry-loader-fxcQDWv8.js line 40 - const scope = options?.scope ?? "all"

Bundle filenames will rotate per release.

Impact and severity

Affected: anything that shells out to openclaw message send. On this install:

  • boot-check posts two reports to Discord per gateway restart, costing ~5 minutes of CPU between them
  • Family cron jobs (chore reports, football updates) each pay 2-3 min when they fire
  • Manual openclaw message send from the shell takes 2-3 min before the message even leaves

Severity: high on slow hardware, medium elsewhere. CPU is taken from whatever else is running, so on a Pi 500 this competes with the gateway's own startup work after a restart and stretches channel startup out further than necessary.

Frequency: every CLI invocation.

Additional information

I worked around this on my box with a one-line dist patch in register.message-*.js:

ensurePluginRegistryLoaded(opts.channel ? { onlyPluginIds: [opts.channel] } : { scope: "configured-channels" });

CLI message send dropped from 2m30s to ~10s.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions