Skip to content

fix(gateway): exec approvals CLI shows clear error when node lacks approvals capability#97585

Closed
Sanjays2402 wants to merge 1 commit into
openclaw:mainfrom
Sanjays2402:fix/issue-97578
Closed

fix(gateway): exec approvals CLI shows clear error when node lacks approvals capability#97585
Sanjays2402 wants to merge 1 commit into
openclaw:mainfrom
Sanjays2402:fix/issue-97578

Conversation

@Sanjays2402

Copy link
Copy Markdown
Contributor

Fixes #97578

What Problem This Solves

Fixes an issue where operators running openclaw approvals get --node <node-id> --json would see a raw Cannot read properties of undefined (reading 'socket') TypeError when the selected node host did not advertise the system.execApprovals.get / system.execApprovals.set commands. The CLI is the affected surface, but the root cause is the Gateway exec-approvals relay accepting any connected node without checking its declared command surface.

The reporter's case: a Windows node connected and advertised system.run, system.run.prepare, and system.which, but not system.execApprovals.get / system.execApprovals.set. The relay still forwarded the call, the node returned no payload, and the CLI's renderer tried to read file.socket?.path on undefined, surfacing an internal stack trace instead of the real operator problem.

Why This Change Was Made

Adds a narrow Gateway-boundary preflight inside respondWithExecApprovalsNodePayload for both exec.approvals.node.get and exec.approvals.node.set. When nodeRegistry.get(nodeId) returns a session, the relay now checks that the node's declared commands include the target system.execApprovals.* method before invoking. If the command is not declared, the relay returns a structured INVALID_REQUEST error with:

  • message: names the node id, the platform, and the missing system.execApprovals.* command, with the same wording the docs use for --node targets ("the node must advertise … or edit the node host approvals file directly").
  • details.reason: "command not declared by node" — the same reason string node.invoke already returns via isNodeCommandAllowed, so existing client error mapping keeps working.
  • details.command / details.nodeId: machine-readable fields for --json consumers.

This mirrors the existing invariant in src/gateway/server-methods/nodes.ts for the generic node.invoke path; the approvals relay was the only sibling that skipped it.

Boundaries:

  • The check only short-circuits when the node session is registered. If the session is not yet known (cold/wake path), the relay still falls through to nodeRegistry.invoke so existing wake / NOT_CONNECTED handling is unchanged.
  • No new flag, no new config, and no change to the node host or the local exec.approvals.{get,set} paths.
  • The local renderer that crashed (file.socket?.path) is intentionally not touched; with the preflight in place the gateway no longer returns an empty payload here, and changing the renderer would broaden scope past the reported defect.

User Impact

  • Operators running openclaw approvals get --node <node-id> against a node that lacks exec-approvals capability now see a clear, actionable message telling them which RPC the node must advertise and that they can edit the node-local approvals file as a workaround, instead of a raw Cannot read properties of undefined (reading 'socket') TypeError.
  • --json consumers get the same information in error.code (INVALID_REQUEST), error.message, and error.details.{reason,command,nodeId} — so dashboards and scripts can recognize the capability mismatch and route the operator to the correct remediation.
  • No behavior change when the node does advertise the approvals commands (existing happy path preserved by test).
  • No behavior change when the node is offline / not registered (the existing wake + NOT_CONNECTED path still runs, asserted by test).

Evidence

Added regression coverage in src/gateway/server-methods/exec-approvals.test.ts. The 4 new tests fail on main and pass with this change (verified both directions locally by stashing the source change and re-running):

  • short-circuits exec.approvals.node.get with INVALID_REQUEST when the node does not declare system.execApprovals.get — reproduces the reported scenario (Windows node, commands: [system.run, system.run.prepare, system.which]). Asserts the relay never reaches nodeRegistry.invoke, the response is INVALID_REQUEST, the message mentions system.execApprovals.get, the node id, and the platform, and details.reason === "command not declared by node".
  • short-circuits exec.approvals.node.set with INVALID_REQUEST when the node does not declare system.execApprovals.set — covers the write path (node declares get but not set).
  • forwards exec.approvals.node.get to the node when system.execApprovals.get is declared — locks in the happy path so the preflight does not regress the normal case.
  • falls through to invoke when the node session is not registered (preserves NOT_CONNECTED wake/error mapping) — confirms the change does not interfere with the cold-node wake path that depends on nodeRegistry.invoke returning NOT_CONNECTED.

Local runs (Vitest is project-sharded; this file runs in both gateway-client and gateway-methods projects):

$ node scripts/run-vitest.mjs src/gateway/server-methods/exec-approvals.test.ts
 ✓ gateway-client   src/gateway/server-methods/exec-approvals.test.ts (6 tests)
 ✓ gateway-methods  src/gateway/server-methods/exec-approvals.test.ts (6 tests)
 Test Files  2 passed (2)
      Tests  12 passed (12)

Adjacent suites still green:

$ node scripts/run-vitest.mjs src/cli/exec-approvals-cli.test.ts
 Test Files  1 passed (1)
      Tests  12 passed (12)

$ node scripts/run-vitest.mjs src/gateway/node-registry.test.ts
 Test Files  4 passed (4)
      Tests  80 passed (80)

Lint and format on the changed files:

$ ./node_modules/oxlint/bin/oxlint src/gateway/server-methods/exec-approvals.ts src/gateway/server-methods/exec-approvals.test.ts
Found 0 warnings and 0 errors.

$ ./node_modules/oxfmt/bin/oxfmt --check src/gateway/server-methods/exec-approvals.ts src/gateway/server-methods/exec-approvals.test.ts
All matched files use the correct format.

Diff scope:

$ git diff origin/main..HEAD --stat
 src/gateway/server-methods/exec-approvals.test.ts | 193 ++++++++++++++++++++++
 src/gateway/server-methods/exec-approvals.ts      |  44 ++++
 2 files changed, 237 insertions(+)

AI disclosure

This change was written with the assistance of Claude. I have reviewed the diff and the test and take responsibility for the code.

…provals capability

The exec.approvals.node.{get,set} gateway relay invoked the node command
without first checking the node's declared command surface. When the node
host advertised system.run but not system.execApprovals.get/set, the node
returned no payload, the CLI cast that to ExecApprovalsSnapshot, and
formatting threw 'Cannot read properties of undefined (reading socket)'
instead of an actionable capability error.

Preflight nodeSession.commands the same way node.invoke does via
isNodeCommandAllowed and return a structured INVALID_REQUEST with
reason 'command not declared by node' and a hint that names the node,
its platform, and the missing system.execApprovals command. The CLI now
surfaces a clear capability mismatch, matching the docs contract for
--node exec approvals targets.

When the node session is not yet registered the relay still falls
through to nodeRegistry.invoke so existing wake / NOT_CONNECTED handling
is unchanged.

Fixes openclaw#97578
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M labels Jun 28, 2026
@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 28, 2026, 7:19 PM ET / 23:19 UTC.

Summary
Adds a Gateway preflight and regression tests for exec.approvals.node.get/set so nodes missing system.execApprovals.* return a structured error.

PR surface: Source +44, Tests +193. Total +237 across 2 files.

Reproducibility: yes. from source, though not live in this read-only review: current main relays exec.approvals.node.get/set without a command-surface check, and the PR's new check uses a filtered command list that does not currently preserve exec-approval commands.

Review metrics: 1 noteworthy metric.

  • New capability gate: 1 added. The new preflight gates on a filtered node command surface, so maintainers need to confirm capable nodes are not rejected before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #97578
Summary: This PR is the candidate fix for the linked clear-error bug; the open command-advertisement PR is an adjacent dependency rather than a replacement.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Fix the command-surface dependency so capable desktop nodes are not rejected by the new preflight.
  • [P1] Add redacted terminal output, copied live output, or logs from an after-fix CLI/Gateway run; redact private details before posting.
  • After updating the PR body with proof, let ClawSweeper re-review automatically or ask a maintainer to comment @clawsweeper re-review.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides unit-test, lint, and format output, but no redacted after-fix CLI/Gateway run or log showing the real operator-visible error path. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Merging as-is can make capable desktop nodes return INVALID_REQUEST before nodeRegistry.invoke because current main does not preserve system.execApprovals.get/set in nodeSession.commands.
  • [P1] The external-contributor proof gate is not met; the PR shows tests/lint/format only, not a redacted after-fix CLI/Gateway run or logs.

Maintainer options:

  1. Fix the command-surface dependency (recommended)
    Preserve or otherwise reliably detect exec-approval-capable node hosts before enforcing this unsupported-capability preflight.
  2. Pause behind the advertisement fix
    If maintainers want fix(gateway): advertise exec approval node commands #88296 to own the command-surface change, pause this PR until that dependency lands and then rebase and retest the preflight.

Next step before merge

  • [P1] The PR needs contributor or maintainer follow-up for the command-surface repair plus real behavior proof; ClawSweeper should not auto-repair while the contributor proof gate remains unmet.

Security
Cleared: The diff changes Gateway error handling and tests only; no dependency, credential, workflow, package, or new code-execution surface was found.

Review findings

  • [P1] Preserve exec-approval commands before preflighting — src/gateway/server-methods/exec-approvals.ts:144
Review details

Best possible solution:

Land a narrow combined fix that returns a structured unsupported-capability error while preserving capable desktop node approval management, then add real CLI/Gateway proof.

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

Yes from source, though not live in this read-only review: current main relays exec.approvals.node.get/set without a command-surface check, and the PR's new check uses a filtered command list that does not currently preserve exec-approval commands.

Is this the best way to solve the issue?

No, not as submitted. Gateway-boundary validation is the right layer, but it must be aligned with node command reconciliation so capable nodes are not misclassified as unsupported.

Full review comments:

  • [P1] Preserve exec-approval commands before preflighting — src/gateway/server-methods/exec-approvals.ts:144
    This check gates on nodeSession.commands, but current main filters desktop node commands through policy that does not include system.execApprovals.get/set. A node host that implements these RPCs can currently still work because nodeRegistry.invoke sends the command directly; after this patch it is rejected before invoking. Please make exec-approval commands survive reconciliation or use a reliable declared-capability source before adding this fail-closed guard.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 19cac35a06ed.

Label changes

Label justifications:

  • P2: This is a normal-priority Gateway/CLI operator workflow bug with limited blast radius, despite a blocking compatibility issue in the current PR.
  • merge-risk: 🚨 compatibility: The diff can break existing capable desktop-node approval management by rejecting before nodeRegistry.invoke when current command policy filters the command out.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides unit-test, lint, and format output, but no redacted after-fix CLI/Gateway run or log showing the real operator-visible error path. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +44, Tests +193. Total +237 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 44 0 +44
Tests 1 193 0 +193
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 237 0 +237

What I checked:

  • Repository policy read: Root and scoped Gateway AGENTS.md files were read fully; the whole-path review and Gateway hot-path guidance affected the review. (AGENTS.md:1, 19cac35a06ed)
  • Scoped Gateway policy read: The Gateway and server-method scoped AGENTS.md files were read; no maintainer notes matched this non-Telegram Gateway change. (src/gateway/AGENTS.md:1, 19cac35a06ed)
  • PR guard location: The PR rejects before invoking when nodeSession.commands does not include the requested exec-approval command. (src/gateway/server-methods/exec-approvals.ts:144, 2c52367985c9)
  • Current command policy dependency: Current main's desktop command policy includes NODE_SYSTEM_RUN_COMMANDS, notify, browser proxy, and screen commands, but not NODE_EXEC_APPROVALS_COMMANDS, so exec-approval IDs are not preserved in the filtered desktop host command set. (src/gateway/node-command-policy.ts:55, 19cac35a06ed)
  • Connect reconciliation filters commands: Node connect reconciliation normalizes declared commands through the pairing allowlist, so commands missing from policy are removed before they become effective runtime commands. (src/gateway/node-connect-reconcile.ts:127, 19cac35a06ed)
  • Current relay behavior: Current main's exec-approvals node relay validates params and then calls nodeRegistry.invoke without checking the connected node command surface. (src/gateway/server-methods/exec-approvals.ts:115, 19cac35a06ed)

Likely related people:

  • martins-oss: Authored recent merged exec-approvals Gateway persistence-error work that changed the same handler and adjacent tests. (role: recent area contributor; confidence: medium; commits: 904c8717d0bb, 9cdcb9c1acf3; files: src/gateway/server-methods/exec-approvals.ts, src/gateway/server-methods/exec-approvals.test.ts)
  • vincentkoc: Authored the open exec-approval command advertisement PR and prior exec-approvals node invoke refactor work adjacent to this dependency. (role: adjacent command-surface contributor; confidence: high; commits: 486d285c24f1, c6232347dcb2, d7d4852e5e34; files: src/gateway/node-command-policy.ts, src/gateway/node-connect-reconcile.ts, src/gateway/server-methods/exec-approvals.ts)
  • steipete: Path history shows repeated Gateway node policy, exec approvals, and docs work that defines the relevant workflow and contracts. (role: feature-history contributor; confidence: high; commits: 9a100d520d93, 2da49ef4acbf, a84910be9149; files: src/gateway/server-methods/exec-approvals.ts, src/gateway/node-command-policy.ts, docs/nodes/index.md)
  • Masato Hoshino: Local blame for the current imported main snapshot points the relevant helper and command-policy lines to a large current-main carrier commit, but the commit subject is unrelated so this is weak routing evidence. (role: current code carrier; confidence: low; commits: cd6d0f9b00f0; files: src/gateway/server-methods/exec-approvals.ts, src/gateway/node-command-policy.ts, src/gateway/node-registry.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 28, 2026
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Superseded by the stronger landed fix #100505 / fccb888.

The landed implementation keeps this PR's dedicated relay preflight, but checks the effective approved session surface and the current Gateway allowlist rather than only raw command advertisement. It also includes the paired desktop command-preservation fix needed for capable nodes to succeed.

Thank you @Sanjays2402 for the careful report and regression work.

@steipete steipete closed this Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: OpenClaw CLI should handle node lacks exec approvals capability gracefully

2 participants