Skip to content

fix(cli): error on unknown command root when --help/--version is appended (#81077)#81083

Merged
steipete merged 1 commit into
openclaw:mainfrom
YB0y:fix/cli-unknown-command-help-bypass
May 25, 2026
Merged

fix(cli): error on unknown command root when --help/--version is appended (#81077)#81083
steipete merged 1 commit into
openclaw:mainfrom
YB0y:fix/cli-unknown-command-help-bypass

Conversation

@YB0y

@YB0y YB0y commented May 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #81077.

Summary

openclaw <unknown-command> --help silently fell through to generic top-level help and exited 0, while the same openclaw <unknown-command> (no --help) correctly errored with Unknown command: openclaw <name>. No built-in command or plugin CLI metadata owns "<name>" and exited 1. The asymmetry is at src/cli/run-main.ts:369-377 and the gated caller at line 494, both of which short-circuited the unknown-primary check when invocation.hasHelpOrVersion was true.

This PR:

  • Drops the invocation.hasHelpOrVersion || bail in resolveUnownedCliPrimary so the helper continues to validate the primary when --help or --version is present.
  • Adds an unconditional unknown-primary check inside runCli after the root and browser help fast paths, before any other dispatch. Legitimate help paths (openclaw --help, openclaw status --help, openclaw browser --help, etc.) keep working through the precomputed fast paths or the standard built-in/plugin help routing; unknown roots now error consistently regardless of --help/--version.
  • Adds two regression tests in src/cli/run-main.exit.test.ts paralleling the existing no-help unknown-root test.

Real behavior proof

  • Behavior or issue addressed: openclaw <unknown-command> --help silently rendered generic top-level help and exited 0, hiding from the user that the command they typed does not exist; the same invocation without --help correctly errored and exited 1. See [Bug]: openclaw <unknown-command> --help silently shows generic top-level help (exit 0); same command without --help correctly errors with Unknown command (exit 1) #81077.

  • Real environment tested: Local source checkout rebased on upstream/main, pnpm build against the working tree, Node v22.22.2 via nvm, pnpm 10.33.0, Linux x86_64 (Ubuntu noble). Real CLI invocation via node openclaw.mjs <args> so the wrapper is exercised the same way an installed npm global runs.

  • Exact steps or command run after this patch:

    pnpm build
    node openclaw.mjs heartbeat > /tmp/no-help.out 2>&1; echo "exit=$?"
    node openclaw.mjs heartbeat --help > /tmp/help.out 2>&1; echo "exit=$?"
    node openclaw.mjs heartbeat --version > /tmp/version.out 2>&1; echo "exit=$?"
    node openclaw.mjs --help > /tmp/root-help.out 2>&1; echo "exit=$?"
    node openclaw.mjs status --help > /tmp/status-help.out 2>&1; echo "exit=$?"
    node openclaw.mjs browser --help > /tmp/browser-help.out 2>&1; echo "exit=$?"
    
  • Evidence after fix: Verbatim terminal output (stdout+stderr merged) from the local CLI:

    ## openclaw heartbeat (unchanged baseline)
    [openclaw] Could not start the CLI.
    [openclaw] Reason: Unknown command: openclaw heartbeat. No built-in command or plugin CLI metadata owns "heartbeat".
    [openclaw] Debug: set OPENCLAW_DEBUG=1 to include the stack trace.
    [openclaw] Try: openclaw doctor
    [openclaw] Help: openclaw --help
    exit=1
    
    ## openclaw heartbeat --help (FIX target)
    [openclaw] Could not start the CLI.
    [openclaw] Reason: Unknown command: openclaw heartbeat. No built-in command or plugin CLI metadata owns "heartbeat".
    [openclaw] Debug: set OPENCLAW_DEBUG=1 to include the stack trace.
    [openclaw] Try: openclaw doctor
    [openclaw] Help: openclaw --help
    exit=1
    
    ## openclaw heartbeat --version (also fixed by the same code path)
    [openclaw] Could not start the CLI.
    [openclaw] Reason: Unknown command: openclaw heartbeat. No built-in command or plugin CLI metadata owns "heartbeat".
    [openclaw] Debug: set OPENCLAW_DEBUG=1 to include the stack trace.
    [openclaw] Try: openclaw doctor
    [openclaw] Help: openclaw --help
    exit=1
    
    ## openclaw --help (legitimate root help — still works)
    🦞 OpenClaw 2026.5.12-beta.1 (d0c2cce) — All your chats, one OpenClaw.
    ...
    exit=0
    
    ## openclaw status --help (legitimate built-in help — still works)
    🦞 OpenClaw 2026.5.12-beta.1 (d0c2cce) — All your chats, one OpenClaw.
    ...
    exit=0
    
    ## openclaw browser --help (legitimate plugin help — still works via precomputed fast path)
    🦞 OpenClaw 2026.5.12-beta.1 (d0c2cce) — All your chats, one OpenClaw.
    ...
    exit=0
    
  • Observed result after fix: openclaw heartbeat, openclaw heartbeat --help, and openclaw heartbeat --version all error consistently with the same Unknown command message and all exit 1. openclaw --help, openclaw status --help, and openclaw browser --help still render their respective help and exit 0. This matches the bot's acceptance criteria on [Bug]: openclaw <unknown-command> --help silently shows generic top-level help (exit 0); same command without --help correctly errors with Unknown command (exit 1) #81077 ("Manual/source run after fix … openclaw heartbeat --help; echo exit=$? should show the unknown-command error and exit 1") and preserves the existing built-in / precomputed-help paths.

  • What was not tested: A live invocation through an installed npm install -g openclaw global binary. The proof above uses the in-tree wrapper node openclaw.mjs, which exercises the same runCli dispatch path the installed binary runs against the built dist. The unit tests in src/cli/run-main.exit.test.ts also pin the rejection behavior through the mocked program builder.

Verification

pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts
# Test Files 2 passed (2)
# Tests 76 passed (76)

pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts
# clean

pnpm check:changed
# (see CI)

@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels May 12, 2026
@clawsweeper

clawsweeper Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Summary
The PR changes CLI dispatch to reject unowned command roots before generic help/version routing and adds regression tests for unknown roots with --help and --version.

Reproducibility: yes. source-reproducible: current main skips unknown-primary validation when hasHelpOrVersion is true and also gates the early caller off for help/version invocations. The linked issue and PR proof match that code path for openclaw heartbeat --help.

Real behavior proof
Sufficient (terminal): The PR body includes after-fix terminal output from a real built source checkout exercising the CLI wrapper and showing corrected exit codes.

Next step before merge
No repair lane is needed because the PR already contains the narrow code/test fix and should proceed through normal maintainer review and checks.

Security
Cleared: The diff only touches CLI dispatch TypeScript and colocated tests, with no concrete security or supply-chain concern.

Review details

Best possible solution:

Land the focused CLI dispatch fix after normal required checks, preserving root/browser help fast paths and the new regression coverage.

Do we have a high-confidence way to reproduce the issue?

Yes, source-reproducible: current main skips unknown-primary validation when hasHelpOrVersion is true and also gates the early caller off for help/version invocations. The linked issue and PR proof match that code path for openclaw heartbeat --help.

Is this the best way to solve the issue?

Yes. The PR targets the dispatch gate directly and places validation after legitimate root/browser help fast paths, which is narrower than changing Commander help rendering or broad CLI registration behavior.

What I checked:

  • Current-main source bug: On current origin/main, resolveUnownedCliPrimary returns null whenever invocation.hasHelpOrVersion is true, so unknown roots with appended help/version skip the unknown-primary check. (src/cli/run-main.ts:370, b4284271082f)
  • Current-main caller gate: The existing early unknown-root caller is guarded by !isHelpOrVersionInvocation, leaving help/version invocations to later generic routing. (src/cli/run-main.ts:494, b4284271082f)
  • Fast-path preservation point: Root help and browser help have explicit fast-path predicates, which the PR places ahead of the new unconditional unknown-root rejection. (src/cli/run-main-policy.ts:41, b4284271082f)
  • PR implementation: At PR head, the helper no longer bails on help/version, and runCli checks for an unowned primary after root/browser help fast paths and before later dispatch. (src/cli/run-main.ts:543, cc545c5a2524)
  • Regression coverage: The PR adds focused tests that reject foo --help and foo --version, parallel to the existing no-help unknown-root test. (src/cli/run-main.exit.test.ts:507, cc545c5a2524)
  • Real behavior proof: The PR body includes after-fix terminal output from a built source checkout showing node openclaw.mjs heartbeat --help and --version exit 1 while root/status/browser help still exit 0. (cc545c5a2524)

Likely related people:

  • steipete: Recent GitHub file history shows steipete authored/committed unknown-command diagnostic and run-main test work on the same CLI dispatch surface. (role: recent area contributor; confidence: high; commits: 09cffbdfbf6e, 0c50957dbb96, 66190a890c29; files: src/cli/run-main.ts, src/cli/run-main.exit.test.ts, src/cli/run-main-policy.ts)
  • vincentkoc: Recent history on the same files includes help/proxy startup and disabled CLI root diagnostic changes relevant to this help/version dispatch boundary. (role: adjacent CLI behavior contributor; confidence: medium; commits: e855b9c8d926, 0eb6848c7c08; files: src/cli/run-main.ts, src/cli/run-main-policy.ts, src/cli/run-main.exit.test.ts)
  • shakkernerd: Recent file history and local blame boundary show adjacent run-main.exit.test.ts and CLI startup routing work, useful for reviewing the regression coverage shape. (role: recent adjacent test contributor; confidence: medium; commits: 85d571823627, 2ff3514037a0; files: src/cli/run-main.exit.test.ts, src/cli/run-main.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against b4284271082f.

Re-review progress:

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
@YB0y YB0y force-pushed the fix/cli-unknown-command-help-bypass branch from c80823c to cc545c5 Compare May 12, 2026 19:50
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
…nded (openclaw#81077)

openclaw <unknown-command> --help silently fell through to generic top-level help and exited 0, while the same openclaw <unknown-command> (no --help) correctly errored with "Unknown command" and exited 1. The asymmetry was caused by two short-circuits on invocation.hasHelpOrVersion: resolveUnownedCliPrimary bailed before the unknown-primary check, and the only caller was gated on !isHelpOrVersionInvocation.

Drop the hasHelpOrVersion bail in resolveUnownedCliPrimary so the helper continues to validate the primary when --help or --version is present, and add an unconditional caller in runCli after the root and browser help fast paths so legitimate help (openclaw --help, openclaw status --help, openclaw browser --help) still dispatches normally while unknown roots error consistently regardless of trailing flags.

Verified:
- pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts (76 passed)
- pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts (clean)
- pnpm build && node openclaw.mjs heartbeat --help (exit 1, Unknown command error)
- pnpm build && node openclaw.mjs status --help (exit 0, status help renders)
- pnpm build && node openclaw.mjs browser --help (exit 0, browser help renders)
- pnpm build && node openclaw.mjs --help (exit 0, root help renders)

Closes openclaw#81077
@steipete steipete force-pushed the fix/cli-unknown-command-help-bypass branch from cc545c5 to c6c4d71 Compare May 25, 2026 22:31
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 25, 2026
@steipete steipete merged commit bec7d56 into openclaw:main May 25, 2026
100 of 101 checks passed
@YB0y

YB0y commented May 26, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @steipete

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 26, 2026
…YB0y)

Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.

Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.

Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.

Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.

Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.

What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.

Co-authored-by: YB0y <brianandez6@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
…YB0y)

Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.

Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.

Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.

Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.

Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.

What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.

Co-authored-by: YB0y <brianandez6@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
…YB0y)

Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.

Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.

Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.

Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.

Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.

What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.

Co-authored-by: YB0y <brianandez6@gmail.com>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…YB0y)

Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.

Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.

Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.

Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.

Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.

What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.

Co-authored-by: YB0y <brianandez6@gmail.com>
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
…YB0y)

Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.

Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.

Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.

Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.

Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.

What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.

Co-authored-by: YB0y <brianandez6@gmail.com>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
…YB0y)

Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.

Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.

Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.

Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.

Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.

What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.

Co-authored-by: YB0y <brianandez6@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes proof: supplied External PR includes structured after-fix real behavior proof. size: XS

Projects

None yet

2 participants