openclaw - ✅(Solved) Fix [Bug]: parent CLI commands exit 1 when invoked without a subcommand (memory, channels, plugins, approvals, devices, cron, mcp) [2 pull requests, 2 comments, 1 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#73077Fetched 2026-04-28 06:27:47
View on GitHub
Comments
2
Participants
1
Timeline
18
Reactions
0
Author
Participants
Timeline (top)
referenced ×4cross-referenced ×3mentioned ×3subscribed ×3

Several openclaw <parent> commands exit with code 1 when invoked with no subcommand, while printing the same help-style content that openclaw <parent> --help prints (which exits 0). This breaks shell && chains, surfaces a misleading ELIFECYCLE Command failed with exit code 1. line under what looks like normal help output, and is inconsistent across the CLI surface — agents and sessions handle the bare form correctly (exit 0 with a default action), but memory, channels, plugins, approvals, devices, cron, and mcp all exit 1.

Root Cause

  • Not exercised in this repro: provider auth (skipped because openclaw models auth login requires an interactive TTY in this environment). The bug is independent of provider state — the affected commands return before any model call.

Fix Action

Fixed

PR fix notes

PR #73116: fix(cli): exit 0 when invoking parent commands without a subcommand (#73077)

Description (problem / solution / changelog)

Fixes #73077.

What

Several `openclaw <parent>` commands (channels, plugins, approvals, devices, cron, mcp) were exiting with code 1 when invoked bare, while printing the same help-style content that `<parent> --help` produces (which exits 0). Reproduced locally — Commander's default behavior for a parent with subcommands is to set `process.exitCode = 1` synchronously, which differs from `--help` (exit 0). This breaks shell `&&` chains and surfaces a misleading `ELIFECYCLE Command failed with exit code 1.` line under pnpm.

Approach

Adopt the shape suggested in the issue thread by @iot2edge — a small shared helper in `src/cli/program/parent-default-help.ts`:

```ts export function applyParentDefaultHelpAction(parent: Command): void { if ((parent as Command & { _actionHandler?: unknown })._actionHandler) { return; // parent already has its own default action — leave it alone } parent.action(() => { parent.outputHelp(); process.exitCode = 0; }); } ```

The no-op-when-action-exists check is what keeps existing intentional defaults intact — e.g. `agents` already defaults to `agents list` (`src/cli/program/register.agent.ts:278`), and `sessions` has its own default. Those are not touched.

Coverage

Applied to the six core parents listed in the issue. `memory` is plugin-defined (manifest `commandAliases`), so it lives outside this PR's surface — but the helper is exported from `src/cli/program/` and is available for future plugin-side fixes.

Files

  • `src/cli/program/parent-default-help.ts` — new helper.
  • `src/cli/program/parent-default-help.test.ts` — 3 cases (default fires + exit 0; existing action wins; subcommand still routes).
  • `src/cli/channels-cli.ts`, `src/cli/plugins-cli.ts`, `src/cli/exec-approvals-cli.ts`, `src/cli/devices-cli.ts`, `src/cli/cron-cli/register.ts`, `src/cli/mcp-cli.ts` — call site each.
  • `CHANGELOG.md`.

Verification

  • New test: `pnpm vitest run src/cli/program/parent-default-help.test.ts` → 3/3 passing.
  • Regression: `pnpm vitest run src/cli/program/{command-tree,register.subclis,build-program,help}.test.ts` → 30/30 passing.
  • Repro confirmed locally that bare `Commander.parseAsync(...)` on a parent with subcommands sets `process.exitCode = 1` without entering `exitOverride` — matches the bug report and `agents` / `sessions` reference behavior.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/memory-core/src/cli.ts (modified, +5/-0)
  • src/cli/channels-cli.ts (modified, +3/-0)
  • src/cli/cron-cli/register.ts (modified, +3/-0)
  • src/cli/devices-cli.ts (modified, +3/-0)
  • src/cli/exec-approvals-cli.ts (modified, +3/-0)
  • src/cli/mcp-cli.ts (modified, +3/-0)
  • src/cli/plugins-cli.ts (modified, +3/-0)
  • src/cli/program/parent-default-help.test.ts (added, +44/-0)
  • src/cli/program/parent-default-help.ts (added, +22/-0)

PR #73155: fix(cli): bare parent commands print help with exit 0 (#73077)

Description (problem / solution / changelog)

Fixes #73077

Problem

Parent CLI commands like openclaw memory, openclaw mcp, openclaw channels, openclaw plugins, openclaw approvals, openclaw devices, and openclaw cron exit with code 1 when invoked without a subcommand, treating it as a usage error. This breaks shell chains:

openclaw memory && echo ok   # never prints "ok" today

Meanwhile openclaw agents and openclaw sessions already exit 0 with a useful default action — so behavior is inconsistent.

Repro on 2026.4.23:

$ openclaw mcp >/dev/null; echo $?
1
$ openclaw agents >/dev/null; echo $?
0

Fix

Introduce attachBareCommandHelp(cmd) in src/cli/program/bare-command-help.ts. It installs a default .action() on the parent that calls cmd.help({ error: false }) — prints help to stdout and exits with code 0, matching the user-friendly pattern.

Apply it to the seven affected parents:

  • memory (extensions/memory-core)
  • mcp
  • channels
  • plugins
  • approvals (exec-approvals)
  • devices
  • cron

The helper is also re-exported from src/memory-host-sdk/runtime-cli.ts (openclaw/plugin-sdk/memory-core-host-runtime-cli) so the bundled memory-core extension can use it without reaching into internal paths.

Note: openclaw message exhibits the same bug but explicitly opts into { error: true }. Left as-is in this PR — issue only enumerates the seven parents above. Happy to fold it in if maintainers prefer.

Tests

New file src/cli/program/bare-command-help.test.ts covers:

  • bare invocation prints help and exits cleanly
  • subcommand invocation is unaffected
  • regression test: explicit expect(caught.exitCode).toBe(0) (would fail without the helper, since commander defaults to 1 for parents-with-subcommands invoked bare)
✓ src/cli/program/bare-command-help.test.ts (4 tests) 105ms

Existing mcp-cli.test.ts, devices-cli.test.ts, exec-approvals-cli.test.ts, plugins-cli.install.test.ts (44), cron-cli/shared.test.ts (21) all still pass.

Notes

  • 10-line helper, ~2 lines per callsite — minimal surface
  • No behavior change for subcommand invocations
  • No public API change (helper is internal to the program directory)

Changed files

  • extensions/memory-core/src/cli.ts (modified, +2/-0)
  • src/cli/channels-cli.ts (modified, +2/-0)
  • src/cli/cron-cli/register.ts (modified, +2/-0)
  • src/cli/devices-cli.ts (modified, +2/-0)
  • src/cli/exec-approvals-cli.ts (modified, +2/-0)
  • src/cli/mcp-cli.ts (modified, +2/-0)
  • src/cli/plugins-cli.ts (modified, +2/-0)
  • src/cli/program/bare-command-help.test.ts (added, +101/-0)
  • src/cli/program/bare-command-help.ts (added, +23/-0)
  • src/memory-host-sdk/runtime-cli.ts (modified, +1/-0)

Code Example

=== Build under test (grounds version + commit) ===

$ pnpm openclaw --version
2026.4.26
$ git rev-parse --short HEAD
cce0c28
$ node --version
v22.22.2

=== Bare-parent vs --help exit code matrix (single shell session) ===

$ for cmd in memory channels plugins approvals devices cron mcp agents sessions; do
    pnpm openclaw "$cmd" >/dev/null 2>&1; echo "openclaw $cmd -> $?"
  done
openclaw memory -> 1
openclaw channels -> 1
openclaw plugins -> 1
openclaw approvals -> 1
openclaw devices -> 1
openclaw cron -> 1
openclaw mcp -> 1
openclaw agents -> 0
openclaw sessions -> 0

$ for cmd in memory channels plugins approvals devices cron mcp; do
    pnpm openclaw "$cmd" --help >/dev/null 2>&1; echo "openclaw $cmd --help -> $?"
  done
openclaw memory --help -> 0
openclaw channels --help -> 0
openclaw plugins --help -> 0
openclaw approvals --help -> 0
openclaw devices --help -> 0
openclaw cron --help -> 0
openclaw mcp --help -> 0

=== Sample bare-parent output (showing help-style content + ELIFECYCLE failure line) ===

$ pnpm openclaw memory
> openclaw@2026.4.26 openclaw /home/orin/Gittensor/Test/openclaw
> node scripts/run-node.mjs memory

  openclaw memory status
    Show index and provider status.
  openclaw memory status --fix
    Repair stale recall locks and normalize promotion metadata.
  ... (truncated; full subcommand example list)
  openclaw memory status --json
    Output machine-readable JSON (good for scripts).

Docs: https://docs.openclaw.ai/cli/memory

 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw channels
... (subcommand list)
Docs: https://docs.openclaw.ai/cli/channels

 ELIFECYCLE  Command failed with exit code 1.

=== Counter-example: agents/sessions exit 0 with a sensible default action ===

$ pnpm openclaw agents
... (prints routing default + tip lines)
  Routing: default (no explicit rules)
$ echo $?
0

$ pnpm openclaw sessions
Sessions listed: 1
Kind   Key                        Age       Model           Tokens (ctx %)       Flags
direct agent:main:main            2m ago    claude-opus-4-7 35k/1049k (3%)       system id:bd65b76e-5383-4e24-9838-3ba4bce5ff87
$ echo $?
0
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Several openclaw <parent> commands exit with code 1 when invoked with no subcommand, while printing the same help-style content that openclaw <parent> --help prints (which exits 0). This breaks shell && chains, surfaces a misleading ELIFECYCLE Command failed with exit code 1. line under what looks like normal help output, and is inconsistent across the CLI surface — agents and sessions handle the bare form correctly (exit 0 with a default action), but memory, channels, plugins, approvals, devices, cron, and mcp all exit 1.

Steps to reproduce

  1. Fresh checkout of openclaw at v2026.4.26 (commit cce0c28); run pnpm install && pnpm build on Node 22.22.2.
  2. Run pnpm openclaw memory (or any of: channels, plugins, approvals, devices, cron, mcp) with no subcommand and no other args.
  3. Observe ELIFECYCLE Command failed with exit code 1. after the help-style output.
  4. Run echo $? — exit code is 1.
  5. For comparison, run pnpm openclaw memory --help and echo $? — same output, exit code 0.
  6. Confirm sibling parents handle it correctly: pnpm openclaw agents and pnpm openclaw sessions both exit 0.

Expected behavior

openclaw <parent> (no subcommand) should exit 0, matching two grounded references observed in the same CLI on the same build (v2026.4.26 / cce0c28):

  1. openclaw <parent> --help — prints identical help-style content and exits 0. The bare form prints the same text but exits 1.
  2. Sibling parent commands openclaw agents and openclaw sessions — print a sensible default listing for the bare form and exit 0. The seven affected parents differ only in lacking that default action.

Either of those is the known-good baseline; the affected parents should match.

Actual behavior

Bare-parent invocations print help-style content (subcommand list and a docs link) and then exit with code 1. Observed in the same shell on v2026.4.26 / cce0c28, Node 22.22.2:

$ pnpm openclaw memory ... (prints "openclaw memory status" example list, "Docs: https://docs.openclaw.ai/cli/memory") ELIFECYCLE Command failed with exit code 1. $ echo $? 1

Same pattern for channels, plugins, approvals, devices, cron, mcp:

$ for cmd in memory channels plugins approvals devices cron mcp agents sessions; do pnpm openclaw "$cmd" >/dev/null 2>&1; echo "$cmd -> $?" done memory -> 1 channels -> 1 plugins -> 1 approvals -> 1 devices -> 1 cron -> 1 mcp -> 1 agents -> 0 sessions -> 0

User-visible surface in pnpm: the trailing line ELIFECYCLE Command failed with exit code 1. appears under the help text, signalling failure even though the help output rendered normally.

OpenClaw version

2026.4.26

Operating system

Ubuntu 24.04.4

Install method

pnpm dev (source checkout from github.com/openclaw/openclaw; pnpm install + pnpm build; invoked via pnpm openclaw)

Model

anthropic/claude-opus-4-7

Provider / routing chain

N/A — no provider chain is involved. The bug is in the local CLI command tree (Commander default-action wiring); the affected commands return before any gateway, proxy, provider, or model router is contacted. Reproduces with no provider configured and no network access.

Additional provider/model setup details

No response

Logs, screenshots, and evidence

=== Build under test (grounds version + commit) ===

$ pnpm openclaw --version
2026.4.26
$ git rev-parse --short HEAD
cce0c28
$ node --version
v22.22.2

=== Bare-parent vs --help exit code matrix (single shell session) ===

$ for cmd in memory channels plugins approvals devices cron mcp agents sessions; do
    pnpm openclaw "$cmd" >/dev/null 2>&1; echo "openclaw $cmd -> $?"
  done
openclaw memory -> 1
openclaw channels -> 1
openclaw plugins -> 1
openclaw approvals -> 1
openclaw devices -> 1
openclaw cron -> 1
openclaw mcp -> 1
openclaw agents -> 0
openclaw sessions -> 0

$ for cmd in memory channels plugins approvals devices cron mcp; do
    pnpm openclaw "$cmd" --help >/dev/null 2>&1; echo "openclaw $cmd --help -> $?"
  done
openclaw memory --help -> 0
openclaw channels --help -> 0
openclaw plugins --help -> 0
openclaw approvals --help -> 0
openclaw devices --help -> 0
openclaw cron --help -> 0
openclaw mcp --help -> 0

=== Sample bare-parent output (showing help-style content + ELIFECYCLE failure line) ===

$ pnpm openclaw memory
> [email protected] openclaw /home/orin/Gittensor/Test/openclaw
> node scripts/run-node.mjs memory

  openclaw memory status
    Show index and provider status.
  openclaw memory status --fix
    Repair stale recall locks and normalize promotion metadata.
  ... (truncated; full subcommand example list)
  openclaw memory status --json
    Output machine-readable JSON (good for scripts).

Docs: https://docs.openclaw.ai/cli/memory

 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw channels
... (subcommand list)
Docs: https://docs.openclaw.ai/cli/channels

 ELIFECYCLE  Command failed with exit code 1.

=== Counter-example: agents/sessions exit 0 with a sensible default action ===

$ pnpm openclaw agents
... (prints routing default + tip lines)
  Routing: default (no explicit rules)
$ echo $?
0

$ pnpm openclaw sessions
Sessions listed: 1
Kind   Key                        Age       Model           Tokens (ctx %)       Flags
direct agent:main:main            2m ago    claude-opus-4-7 35k/1049k (3%)       system id:bd65b76e-5383-4e24-9838-3ba4bce5ff87
$ echo $?
0

Impact and severity

Affected users/systems/channels:

  • Every operator who invokes a parent CLI command without a subcommand to discover what's available — the canonical "what does this command do?" UX path.
  • Every shell script, alias, or CI step that runs a bare parent (e.g. openclaw plugins to print a quick summary) and chains via && or relies on exit codes.
  • Surface confirmed on Linux (Ubuntu 24.04 / Node 22.22.2 / pnpm 10.33.0); CLI surface is platform-agnostic so behavior is expected on macOS/Windows too, though only Linux was directly observed.

Severity:

  • Annoying / DX paper cut. Misleads operators with ELIFECYCLE Command failed with exit code 1. after what looks like normal help output.
  • Workflow-breaking for shell chains: any openclaw <parent> && <next> silently skips <next>. Direct evidence: bare-parent returns exit code 1, which short-circuits && chains by definition.
  • No data risk, no security impact observed — affected commands return before any gateway/provider/network call.

Frequency:

  • Always, deterministic. 7 of 9 parent commands tested reproduce the bug on every invocation in the same shell, with exit codes identical across repeated runs.

Consequence:

  • Misleading failure signal during CLI exploration (new operators see "Command failed" under help text).
  • Silent skips in &&-chained shell scripts and CI steps that include a bare parent for discovery or status.
  • Inconsistent CLI surface between sibling parent commands (agents/sessions vs the seven affected) which costs operator trust and complicates documentation.
  • No grounded evidence of missed messages, failed onboarding, or extra cost.

Additional information

  • Regression status: unknown. I have not bisected. Only v2026.4.26 (commit cce0c28) was directly observed; last-known-good and first-known-bad versions were not measured. Do not classify as a regression without that evidence.

  • Likely fix locus: the per-feature *-cli.ts files in src/cli/ and the parent-command builders under src/cli/program/. The two parents that work correctly (agents, sessions) appear to register their own default .action() that performs a sensible read-only listing; the seven affected parents do not, so Commander.js's built-in fallback prints help and exits non-zero. A single helper that registers .action(() => cmd.outputHelp()) on any parent without a custom action would normalize all of them.

  • Suggested regression test: a table-driven test alongside src/cli/program/command-tree.test.ts / src/cli/program/help.test.ts that walks every registered parent in the program tree and asserts exitCode === 0 when invoked with no args. Locks the contract for any new parent group added later.

  • Dedupe checked against the openclaw issue corpus on 2026-04-28: no existing open or closed issue matches this exact behavior. Closest related and distinct: #59449 (openclaw cron <subcommand> --help showed parent help instead of subcommand help — fixed and closed; different code path).

  • Not exercised in this repro: provider auth (skipped because openclaw models auth login requires an interactive TTY in this environment). The bug is independent of provider state — the affected commands return before any model call.

  • Reporter environment quirk worth noting (not the cause): the doctor-installed systemd user service openclaw-gateway.service had to be pointed at ~/.nvm/versions/node/v22.22.2/bin/node because system Node at /usr/bin/node is v20.20.2 (below the required ≥22.14). Doctor handled this automatically. Mentioned only so reviewers don't suspect a Node version mismatch — Node 22.22.2 was active for every reproduction above.

extent analysis

TL;DR

The issue can be fixed by registering a default action for each parent command that prints help and exits with code 0, similar to the agents and sessions commands.

Guidance

  • Review the *-cli.ts files in src/cli/ and the parent-command builders under src/cli/program/ to identify the code responsible for handling bare parent commands.
  • Implement a helper function that registers a default action for each parent command, similar to the one used by agents and sessions, which performs a sensible read-only listing and exits with code 0.
  • Update the affected parent commands (memory, channels, plugins, approvals, devices, cron, and mcp) to use this helper function.
  • Consider adding a regression test to ensure that all parent commands exit with code 0 when invoked with no args.

Example

// src/cli/program/parent-command-builder.ts
import { Command } from 'commander';

const registerDefaultAction = (cmd: Command) => {
  cmd.action(() => {
    cmd.outputHelp();
    process.exit(0);
  });
};

// Register default action for each parent command
const parentCommands = ['memory', 'channels', 'plugins', 'approvals', 'devices', 'cron', 'mcp'];
parentCommands.forEach((command) => {
  const cmd = new Command(command);
  registerDefaultAction(cmd);
});

Notes

The provided code snippet is a hypothetical example and may need to be adapted to the actual codebase. The registerDefaultAction function should be implemented to match the existing code structure and style.

Recommendation

Apply the workaround by registering a default action for each parent command to ensure consistent behavior across the CLI surface. This fix should be implemented in the *-cli.ts files and the parent-command builders under src/cli/program/.

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…

FAQ

Expected behavior

openclaw <parent> (no subcommand) should exit 0, matching two grounded references observed in the same CLI on the same build (v2026.4.26 / cce0c28):

  1. openclaw <parent> --help — prints identical help-style content and exits 0. The bare form prints the same text but exits 1.
  2. Sibling parent commands openclaw agents and openclaw sessions — print a sensible default listing for the bare form and exit 0. The seven affected parents differ only in lacking that default action.

Either of those is the known-good baseline; the affected parents should match.

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 [Bug]: parent CLI commands exit 1 when invoked without a subcommand (memory, channels, plugins, approvals, devices, cron, mcp) [2 pull requests, 2 comments, 1 participants]