fix(cli): render non-Error failures instead of [object Object] - #1404
Conversation
A top-level rejection that isn't an Error — e.g. a protocol-shaped
`{ status: 401 }` from a broker client — printed `[object Object]` and
nothing else, and an Error with an empty message printed nothing at all.
Add `describeError(unknown): string`: Error -> message (falling back to
name/cause when empty), object -> `message`/`error` text with `status`/
`code` context or compact credential-redacted JSON, primitive -> String.
Use it at the top-level catch, the attach paths, the broker failure
mapper (which reported `[object Object]` as the failure message), and the
per-module error-message helpers.
`broker-lifecycle`'s exported `describeError` is the cause-chain
specialization, renamed to `describeErrorWithCause` and now built on the
shared formatter.
Closes #1383
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AKFr7pXffb5LVaP5pjo8bG
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughA shared ChangesCLI error formatting
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a08d755da3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli/src/cli/lib/describe-error.ts`:
- Around line 99-112: Update toCompactJson so its JSON.stringify fallback
handles BigInt values and does not return the generic Object.prototype.toString
tag for plain objects. When serialization fails, produce a useful
constructor/tag-based description of the unserializable value while preserving
the existing redaction and truncation behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b396f9ee-4623-44c1-b1d0-fe537bf30278
📒 Files selected for processing (14)
CHANGELOG.mdpackages/cli/src/cli/index.tspackages/cli/src/cli/lib/attach-broker.tspackages/cli/src/cli/lib/attach-drive.tspackages/cli/src/cli/lib/attach-native.tspackages/cli/src/cli/lib/attach-passthrough.tspackages/cli/src/cli/lib/broker-lifecycle.test.tspackages/cli/src/cli/lib/broker-lifecycle.tspackages/cli/src/cli/lib/core-maintenance.tspackages/cli/src/cli/lib/describe-error.test.tspackages/cli/src/cli/lib/describe-error.tspackages/cli/src/cli/lib/index.tspackages/cli/src/cli/lib/node-provider-child.tspackages/cli/src/cli/mcp/action-tools.ts
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AKFr7pXffb5LVaP5pjo8bG
…readable
Review follow-ups on the describeError backstop:
- A server error payload is free to echo the credential that was sent to
it, and the `message`/`error` text is lifted out whole — key-name
redaction never sees it. Run every branch's output through
`redactCredentialValues`, the same value-pattern masking the CLI's
commander output boundary already applies.
- `JSON.stringify` throws on BigInt, so `{ status: 401, retryAfter: 30n }`
hit the catch and returned `[object Object]` — the exact rendering this
module exists to eliminate. Serialize BigInt/function/symbol members
through a replacer, and name a genuinely unserializable value by its
constructor instead of the Object tag.
`@agent-relay/cloud` gains a `./redact` subpath so the formatter can take
the masking helper without the package barrel, which pulls in auth and
child-process spawning and broke the doctor suite's module mocks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AKFr7pXffb5LVaP5pjo8bG
Summary
Fixes #1383.
packages/cli/src/cli/index.tsrendered top-level rejections witherr instanceof Error ? err.message : String(err). A non-Errorrejection — a protocol-shaped object like{ status: 401 }from a broker client — printed[object Object]and nothing else. AnErrorwith an empty message printed nothing at all, since the handler only wrote when the string was truthy.Adds
describeError(unknown): string(packages/cli/src/cli/lib/describe-error.ts):Error→ itsmessage; when the message is empty, itsname, orname: <cause>when it carries acausemessage/errortext plusstatus/codecontext (invalid workspace key (status 401, code unauthorized)), else compact JSON ({"status":401})String(value); never returns an empty string, never returns[object Object]The JSON fallback runs through the existing
redactSecrets, so a rejection payload carrying a workspace key or token cannot leak into stderr, and it truncates at 500 chars so a large payload can't flood the terminal. Cycles render as[circular]rather than throwing.Call sites updated:
cli/index.tsattach-broker.tsmapBrokerSdkFailure, which is where the reported attach 401 turned into[object Object]as the failure message that callers then printednode agent attachpaths (attach-native.ts,attach-drive.ts,attach-passthrough.ts)toErrorMessage/errorMessage/extractChildFailurehelpers (core-maintenance.ts,broker-lifecycle.ts,mcp/action-tools.ts,node-provider-child.ts)broker-lifecycle.tsalready exported adescribeError— the cause-chain walker that unwrapsTypeError: fetch failedintoECONNREFUSEDetc. It's the broker-start specialization, so it's renamed todescribeErrorWithCauseand now builds on the shared formatter (gaining non-Errorhandling for free).On the issue's root-cause companion: the broker client paths already reject with typed
HarnessDriverProtocolErrors throughout@agent-relay/harness-driver(HTTP failures, WS input-stream failures, timeouts, closes), and I found no site in this repo that throws or rejects with a bare object. So this ships the backstop formatter only; if the{ status: 401 }value originated outside these paths, the formatter now surfaces enough detail to identify where.Test Plan
packages/cli/src/cli/lib/describe-error.test.tscovers Error/empty-message/cause, protocol-shaped objects, nestederrorobjects, credential redaction, cycles, truncation, primitives, and an explicit "never renders[object Object]" casenpx vitest run packages/cli— 879 passed, 11 skipped, 0 failednpm run typecheck— cleannpm run lint— no new warnings (only pre-existing complexity/naming warnings)Screenshots
n/a
Generated by Claude Code