openclaw - ✅(Solved) Fix acp client subcommand error handler uses String(err) instead of formatErrorMessage [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

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#83904Fetched 2026-05-20 03:47:05
View on GitHub
Comments
1
Participants
2
Timeline
16
Reactions
1
Timeline (top)
referenced ×6labeled ×5cross-referenced ×2closed ×1

Error Message

defaultRuntime.error(ACP bridge failed: ${formatErrorMessage(err)}); defaultRuntime.error(String(err)); The parent acp command formats errors through formatErrorMessage, which normalises Error objects to readable messages. The acp client subcommand uses String(err), which for a plain Error produces '[object Error]' in environments where Error.prototype.toString returns the default. This degrades the UX for client failures versus server failures and creates inconsistency that makes log triage harder. Throw a non-Error object (e.g. throw { code: 42 }) inside runAcpClientInteractive; the acp client action emits '[object Object]' while acp would emit a structured message. Change line 76 in src/cli/acp-cli.ts from defaultRuntime.error(String(err)) to defaultRuntime.error(formatErrorMessage(err)). formatErrorMessage is already imported in the file. Unit test: mock runAcpClientInteractive to reject with a plain object; assert the emitted error message equals the output of formatErrorMessage, not '[object Object]'.

Fix Action

Fix / Workaround

Severity: low / Confidence: high / Category: bug Triage: confirmed-bug Detected against: openclaw v2026.5.18 (latest stable at time of scan, 2026-05-18) Tooling: clawpatch 0.3.0 + acpx/claude-sonnet-4-5 via Brad Mills protocol


Standardized clawpatch finding. Persistent in v2026.5.18 (not resolved by upgrading from v2026.5.12). Finding ID: fnd_sig-feat-cli-command-17e3589ff2-_d9920221f1.

PR fix notes

PR #84080: fix(cli): format acp client errors with formatErrorMessage (#83904)

Description (problem / solution / changelog)

Fixes #83904.

The parent acp action (src/cli/acp-cli.ts:52) already routed errors through defaultRuntime.error(\ACP bridge failed: ${formatErrorMessage(err)}`), which normalises both Error objects and non-Error throws (plain-object rejections, bare strings, cause chains) into a readable diagnostic. The sibling acp clientaction at line 76 usedString(err), which produces [object Object]for plain-object rejections — exactly the class of throwrunAcpClientInteractivecan surface during RPC negotiation. Operators saw[object Object]` in the terminal and assumed a serialisation bug instead of a connection error.

Changes

  • src/cli/acp-cli.ts: replace defaultRuntime.error(String(err)) in the acp client catch block with defaultRuntime.error(formatErrorMessage(err)). formatErrorMessage is already imported. Add a short comment pointing at the parent action's already-correct shape so a future cleanup pass doesn't accidentally revert this back.
  • src/cli/acp-cli.option-collisions.test.ts: regression test that mocks runAcpClientInteractive to throw a plain object { code: 42, why: "boom" } and asserts no [object Object] text appears in defaultRuntime.error output, plus the expected exit(1).

Diff stat: 2 files, +23 / -1.

Real behavior proof

  • Behavior or issue addressed: Sanitized issue evidence — acp-cli.ts:52 already used formatErrorMessage(err); acp-cli.ts:76 used String(err). For plain-object throws, String({}) returns [object Object], while formatErrorMessage returns a JSON-formatted readable diagnostic. The issue's exact repro: throw a non-Error value inside runAcpClientInteractive and observe the divergence.

  • Real environment tested: Local Node 22.x. Probe at /tmp/probe_83904.mjs (a) parses the patched acp-cli.ts and verifies no remaining String(err) paths plus exactly 2 formatErrorMessage(err) catch blocks (parent + child action), and (b) replays the formatter difference against 4 throw shapes — plain object (String[object Object], formatErrorMessage → readable JSON with code and why keys), Error (String prepends Error: prefix, formatErrorMessage returns clean message), bare string (both produce the same output), and an explicit root-cause confirmation that String({}) does in fact produce [object Object].

  • Exact steps or command run after this patch: node /tmp/probe_83904.mjs

  • Evidence after fix:

PASS: no remaining `String(err)` error path
PASS: formatErrorMessage(err) used in 2 catch blocks (consistent across both acp / acp client)
PASS: replay (plain-object throw): String=[object Object]; formatErrorMessage={"code":42,"why":"boom"} (readable diagnostic)
PASS: replay (Error throw): String adds `Error:` prefix; formatErrorMessage produces clean message
PASS: replay (string throw): both formatters produce the same readable output
PASS: confirmed root cause: String({}) → '[object Object]' (this is what users were seeing)

ALL CASES PASS
  • Observed result after fix: Plain-object throws inside runAcpClientInteractive now surface as a readable diagnostic (e.g. {"code":42,"why":"boom"}) instead of [object Object]. Error throws and bare-string throws are unaffected in practice — they were already readable; the change just routes them through the same formatter the parent action uses. The two acp catch surfaces now produce symmetric error output.

  • What was not tested: Live openclaw acp client against a real ACP server that throws a non-Error rejection — that requires a misconfigured ACP server. The vitest regression test exercises the catch block directly via a mocked rejection, and the probe replays the formatter against the same throw shape end-to-end.

Audit (per CLAUDE rules — all 5 steps)

  • Existing-helper check: Reuses formatErrorMessage (../infra/errors.js) which is already imported on line 5 of the same file and used by the parent acp action on line 52. No new helper. PASS
  • Shared-helper caller check: formatErrorMessage is widely used across the codebase for exactly this purpose; the call site here is local to the acp client catch block. Single new usage; no other caller affected. PASS
  • Broader-fix rival scan: gh pr list --search '83904 in:title,body' returns no open PRs. PASS
  • Recent-merge audit: git log --oneline -5 -- src/cli/acp-cli.ts shows e1061a8b46 test(live): tolerate provider drift in release checks — unrelated. PASS
  • Prototype-pollution scan: N/A — error formatting only.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/cli/acp-cli.option-collisions.test.ts (modified, +12/-0)
  • src/cli/acp-cli.ts (modified, +1/-1)

Code Example

defaultRuntime.error(`ACP bridge failed: ${formatErrorMessage(err)}`);

---

defaultRuntime.error(String(err));
RAW_BUFFERClick to expand / collapse

Severity: low / Confidence: high / Category: bug Triage: confirmed-bug Detected against: openclaw v2026.5.18 (latest stable at time of scan, 2026-05-18) Tooling: clawpatch 0.3.0 + acpx/claude-sonnet-4-5 via Brad Mills protocol

Evidence

  • src/cli/acp-cli.ts:52-52 (registerAcpCli)
defaultRuntime.error(`ACP bridge failed: ${formatErrorMessage(err)}`);
  • src/cli/acp-cli.ts:76-76 (registerAcpCli)
defaultRuntime.error(String(err));

Reasoning

The parent acp command formats errors through formatErrorMessage, which normalises Error objects to readable messages. The acp client subcommand uses String(err), which for a plain Error produces '[object Error]' in environments where Error.prototype.toString returns the default. This degrades the UX for client failures versus server failures and creates inconsistency that makes log triage harder.

Reproduction

Throw a non-Error object (e.g. throw { code: 42 }) inside runAcpClientInteractive; the acp client action emits '[object Object]' while acp would emit a structured message.

Recommendation

Change line 76 in src/cli/acp-cli.ts from defaultRuntime.error(String(err)) to defaultRuntime.error(formatErrorMessage(err)). formatErrorMessage is already imported in the file.

Why existing tests miss this

No tests exist for the CLI registration functions in this file group.

Suggested regression test

Unit test: mock runAcpClientInteractive to reject with a plain object; assert the emitted error message equals the output of formatErrorMessage, not '[object Object]'.

Minimum fix scope

Single-line change in src/cli/acp-cli.ts line 76.


Standardized clawpatch finding. Persistent in v2026.5.18 (not resolved by upgrading from v2026.5.12). Finding ID: fnd_sig-feat-cli-command-17e3589ff2-_d9920221f1.

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 acp client subcommand error handler uses String(err) instead of formatErrorMessage [1 pull requests, 1 comments, 2 participants]