openclaw - ✅(Solved) Fix CLI suggests `plugins.allow` for unknown subcommands when input is actually an agent tool name [2 pull requests, 2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#77214Fetched 2026-05-05 05:51:05
View on GitHub
Comments
2
Participants
3
Timeline
6
Reactions
2
Timeline (top)
commented ×2cross-referenced ×2referenced ×2

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.

Error Message

[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)

Root Cause

[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)

Fix Action

Fix / Workaround

openclaw config patch '{"plugins":{"allow":["...","lcm_recent","lossless","lcm","..."]}}'
[tools] gateway failed: gateway config.patch cannot change protected config paths: plugins.allow
  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"

PR fix notes

PR #77220: fix(cli): emit agent-tool diagnostic instead of plugins.allow suggestion

Description (problem / solution / changelog)

Root cause

resolveMissingPluginCommandMessage in src/cli/run-main-policy.ts falls straight through to the plugins.allow suggestion branch when the user types an unrecognized subcommand. It has no awareness of contracts.tools — the tool names a plugin registers for agent model-use. So openclaw wecom_mcp (a tool name, not a CLI command) produces:

The `openclaw wecom_mcp` command is unavailable because `plugins.allow` excludes "wecom_mcp". Add "wecom_mcp" to `plugins.allow` if you want that bundled plugin CLI surface.

That's wrong: wecom_mcp is never a CLI subcommand, and adding it to plugins.allow does nothing useful.

Fix

  • src/plugins/manifest-command-aliases.runtime.ts — adds resolveManifestToolOwner, which loads the manifest registry and searches each plugin's contracts.tools array for a matching tool name.
  • src/cli/run-main-policy.ts — adds resolveToolOwner?: (toolName: string) => string | undefined to the options of resolveMissingPluginCommandMessage; calls it just before the plugins.allow branch. If it returns a plugin ID, emits a targeted diagnostic instead.
  • src/cli/run-main.ts — wires resolveToolOwner: (toolName) => resolveManifestToolOwner({ toolName, config }) at the runtime call site alongside the existing resolveCommandAliasOwner.

Example output after fix

"wecom_mcp" is an agent tool registered by the "wecom" plugin, not a CLI subcommand. Use it from an agent session (model tool-use), not the CLI.

Tests

Two new unit tests in src/cli/run-main.test.ts:

  • emits agent-tool message when resolveToolOwner identifies the tool owner
  • falls through to plugins.allow message when resolveToolOwner returns undefined

All 29 existing tests continue to pass.

Fixes #77214.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/cli/run-main-policy.ts (modified, +8/-0)
  • src/cli/run-main.test.ts (modified, +32/-0)
  • src/cli/run-main.ts (modified, +2/-1)
  • src/plugins/manifest-command-aliases.runtime.ts (modified, +28/-0)

PR #77221: fix(cli): clarify error when unknown subcommand is actually an agent tool name (#77214)

Description (problem / solution / changelog)

Summary

  • When openclaw <name> does not match a CLI subcommand or a plugin id, the unknown-subcommand handler now first looks up <name> against the runtime plugin tool registry. If <name> is an agent tool registered by a loaded plugin (e.g. lcm_recent from lossless-claw), emit a clear "this is an agent tool, not a CLI subcommand" error pointing at model tool-use instead of the misleading plugins.allow suggestion.
  • The previous error told the user to add the tool name to plugins.allow, but plugins.allow accepts plugin ids (not tool names), and config patches against it are rejected by the protected-config-paths guard. In the wild this sent an agent down 3 restart cycles trying to "fix" config that was never the problem.
  • Fall-throughs are preserved: if <name> is not a registered tool, the existing plugins.allow and plugins.entries.<id>.enabled suggestions are emitted unchanged. The new branch only fires when the tool-registry lookup matches.

Reproduction

Before:

[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.

After:

"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. Run
`openclaw --help` to see available CLI subcommands.

Implementation

  • New resolveManifestToolOwnerInRegistry in src/plugins/manifest-command-aliases.ts walks plugins[].contracts.tools (the same field the agent dispatch uses) and returns {toolName, pluginId} for the owning plugin.
  • PluginManifestCommandAliasRegistry is extended with an optional contracts?: { tools?: readonly string[] } per plugin entry. The runtime PluginManifestRegistry already populates this, so production wiring is structural.
  • resolveMissingPluginCommandMessage (in src/cli/run-main-policy.ts) gets a parallel resolveToolOwner callback hook (mirroring resolveCommandAliasOwner) and consults the tool registry just before the plugins.allow excludes branch.
  • src/cli/run-main.ts passes the new resolveManifestToolOwner runtime resolver into the policy call.

Validation

  • pnpm exec vitest run src/cli/run-main.test.ts src/plugins/manifest-command-aliases.test.ts -- 33 passed (3 new in run-main.test.ts, 1 new in manifest-command-aliases.test.ts).
  • pnpm exec vitest run src/cli/run-main.exit.test.ts src/cli/run-main.profile-env.test.ts -- 79 passed (sanity check on adjacent test files).
  • pnpm exec oxlint --type-aware src/cli/run-main-policy.ts src/cli/run-main.ts src/cli/run-main.test.ts src/plugins/manifest-command-aliases.ts src/plugins/manifest-command-aliases.runtime.ts src/plugins/manifest-command-aliases.test.ts -- 0 warnings, 0 errors.
  • pnpm tsgo:core -- clean.
  • pnpm check:changelog-attributions -- clean.

Closes #77214.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/cli/run-main-policy.ts (modified, +25/-0)
  • src/cli/run-main.test.ts (modified, +51/-0)
  • src/cli/run-main.ts (modified, +2/-1)
  • src/plugins/manifest-command-aliases.runtime.ts (modified, +22/-0)
  • src/plugins/manifest-command-aliases.test.ts (modified, +27/-0)
  • src/plugins/manifest-command-aliases.ts (modified, +29/-0)

Code Example

openclaw lcm_recent

---

[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)

---

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

---

[tools] gateway failed: gateway config.patch cannot change protected config paths: plugins.allow
RAW_BUFFERClick to expand / collapse

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 #76950 (fix(sessions): context-engine fallback session-size guard) where this behavior was observed in the wild during operator troubleshooting.

extent analysis

TL;DR

The CLI router should be updated to provide accurate error messages when encountering unknown subcommands, distinguishing between registered agent tools, disabled plugins, and unknown plugin IDs or CLI subcommands.

Guidance

  • Update the unknown-subcommand error handler in src/cli/run-main.ts to perform a lookup against the running plugin tool registry.
  • If the input name matches a registered tool, emit a message indicating it's an agent tool, not a CLI subcommand.
  • If the input name matches a known plugin ID that's currently disabled in plugins.allow, keep the existing suggestion.
  • Otherwise, emit a "did you mean" suggestion against loaded plugin IDs and bundled CLI surface IDs.

Example

// src/cli/run-main.ts
const handleUnknownSubcommand = (inputName: string) => {
  const registeredTool = getRegisteredTool(inputName);
  if (registeredTool) {
    // Emit message indicating it's an agent tool, not a CLI subcommand
    console.log(`"${inputName}" is an agent tool registered by the "${registeredTool.pluginId}" plugin, not a CLI subcommand.`);
  } else {
    const disabledPluginId = getDisabledPluginId(inputName);
    if (disabledPluginId) {
      // Keep existing suggestion
      console.log(`The "${inputName}" command is unavailable because "plugins.allow" excludes "${disabledPluginId}".`);
    } else {
      // Emit "did you mean" suggestion
      const loadedPluginIds = getLoadedPluginIds();
      console.log(`"${inputName}" is not a known plugin ID or CLI subcommand. Did you mean one of the following loaded plugins: ${loadedPluginIds.join(', ')}?`);
    }
  }
};

Notes

The proposed fix should be a small update to the unknown-subcommand error handler, and it preserves the existing suggestion for the case where the input name matches a known plugin ID that's currently disabled in

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix CLI suggests `plugins.allow` for unknown subcommands when input is actually an agent tool name [2 pull requests, 2 comments, 3 participants]