Skip to content

CLI suggests plugins.allow for unknown subcommands when input is actually an agent tool name #77214

@100yenadmin

Description

@100yenadmin

Summary

When a user invokes openclaw <tool-name> where <tool-name> is a registered agent tool owned by an enabled, loaded plugin (not a plugin id and not a bundled CLI surface), the CLI router emits a misleading error that suggests adding the tool name to plugins.allow. The suggestion does nothing — plugins.allow accepts plugin ids, not tool names — and routes the user toward editing config that gets rejected by the protected-paths guard anyway.

Reproduction

A loaded lossless-claw plugin (kind: context-engine) registers five agent tools at runtime via api.registerTool(factory, { name: "lcm_recent" }) etc. The manifest correctly declares them in contracts.tools. The agent attempts:

openclaw lcm_recent

Result:

[openclaw] Failed to start CLI: Error: The `openclaw lcm_recent` command is unavailable
because `plugins.allow` excludes "lcm_recent". Add "lcm_recent" to `plugins.allow`
if you want that bundled plugin CLI surface.
    at runCli (file:///.../dist/cli/run-main.js:412:46)
    at async runMainOrRootHelp (file:///.../dist/entry.js:356:3)

The user (or agent) then follows the suggestion, attempting:

openclaw config patch '{"plugins":{"allow":["...","lcm_recent","lossless","lcm","..."]}}'

Which fails with the protected-config-paths guard:

[tools] gateway failed: gateway config.patch cannot change protected config paths: plugins.allow

— at which point the user thinks the plugin is broken when in fact the plugin works correctly and the tool just isn't a CLI subcommand.

Why the message is wrong

plugins.allow is a list of plugin ids (e.g. lossless-claw, cortex, telegram). It is not a list of tool names. The suggestion to add "lcm_recent" to plugins.allow would produce:

  • A plugin not found: lcm_recent warning at next config load (visible in openclaw doctor)
  • No effect on the actual tool's availability

The CLI router has the information needed to give a much better error. The plugin manifest declares contracts.tools = ["lcm_recent", ...], and the runtime registry knows which plugin owns each tool name. The suggestion should be one of:

  1. "lcm_recent is an agent tool registered by the lossless-claw plugin, not a CLI subcommand. Use it from an agent turn (model tool-use), not the CLI." — accurate when the name maps to a registered tool of an enabled plugin.

  2. "lcm_recent is not a known plugin id or CLI subcommand. Did you mean lossless-claw? (Loaded plugins: ...)" — when the name doesn't match any registered tool either.

The current message conflates "this name is unknown" with "this is a known plugin disabled in plugins.allow" — which is the only case the suggestion to edit plugins.allow would actually help.

Why this is operationally bad

In our case (separate report at #76940 / PR #76950), an agent that hit this error spent 3+ restart cycles attempting to "fix" the perceived configuration problem:

  1. Agent ran openclaw lcm_recent <args>
  2. Hit the misleading error
  3. Tried to add "lcm_recent", "lossless", "lcm" aliases to plugins.allow via config.patch
  4. Hit the protected-paths guard
  5. Concluded the plugin was broken and edited the LCM source to "add explicit tool registration names" (which were already present)
  6. Restarted the gateway twice to "reload"

None of this was needed — the plugin works correctly and the tools are registered. The agent was misled by the CLI error suggestion.

Proposed fix

In src/cli/run-main.ts (or wherever the unknown-subcommand handler lives), before emitting the plugins.allow suggestion:

  1. Look up the input name against the running plugin tool registry (the same lookup the agent's tool-dispatch uses).
  2. If it matches a registered tool: emit the "this is a model tool, not a CLI subcommand" message instead.
  3. If it matches a known plugin id that's currently disabled in plugins.allow: keep the existing suggestion (this is the only case where it's correct).
  4. Otherwise: emit the "did you mean" suggestion against loaded plugin ids and bundled CLI surface ids, NOT against tool names.

The fix should be small (one branch in the unknown-subcommand error handler) and is a strict improvement — the only case where the current suggestion is correct is case (3), which the new logic preserves.

Adjacent

  • This bites users on plugins that register agent tools at runtime (lossless-claw, possibly cortex's tools depending on how they're registered, anything using api.registerTool). It does NOT bite users whose tools are declared statically in the manifest's top-level tools: [...] field — those have a separate code path.
  • Related to PR fix(sessions): add context-engine fallback session-size guard (#76940) #76950 (fix(sessions): context-engine fallback session-size guard) where this behavior was observed in the wild during operator troubleshooting.

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