fix(chat): surface the full error in a copyable block - #2434
Conversation
When a turn fails, the classifier only forwarded the short constructed message into the error envelope's details and capped it at 500 characters, so the gateway's real reason (OpenRouter-style gateways hide it in the response body / metadata.raw) never reached the user. Errors the AI SDK could not classify carried no details at all. Forward the full upstream response body (or structured data.raw), pretty-printed as JSON, into details on every path, redacted and bounded at a larger cap so the whole body survives to be copied. The card now renders that detail always expanded under a Full error heading with a copy control that copies the entire error even when the block clips it on screen, while the classified title and message stay on top. The block is omitted when the detail only repeats the message, so a clean one-line error is not shown twice.
✅ Tests passed: 1211/1214Ran 8 of 15 suites (7 not affected by this change).
|
Greptile SummaryThis follow-up expands chat error details so users can inspect and copy scrubbed upstream responses while retaining the classified error message.
Confidence Score: 3/5This PR should not merge until arbitrary upstream diagnostics are scrubbed without exposing unsupported credential formats in the error card and clipboard. The reply from the unnamed thread participant states that the scrubber was broadened, but the current finite regex list still forwards Basic authorization and cookie/session credentials unchanged, providing a concrete residual disclosure despite that fix claim. Files Needing Attention: packages/browseros-agent/apps/server/src/agent/chat-error.ts
|
| Filename | Overview |
|---|---|
| packages/browseros-agent/apps/server/src/agent/chat-error.ts | Forwards richer upstream diagnostics and broadens redaction, but the replacement scrubber still permits common credential representations into client-visible details. |
| packages/browseros-agent/apps/app/screens/sidepanel/index/ChatError.tsx | Formats, displays, and copies the full details string through the shared error card as intended. |
| packages/browseros-agent/apps/server/tests/agent/chat-error.test.ts | Adds substantial coverage for upstream detail selection, formatting, truncation, and several supported secret formats. |
| packages/browseros-agent/apps/app/screens/sidepanel/index/ChatError.test.tsx | Covers the always-expanded Full error presentation, copy control, duplicate suppression, and preservation of the classified message. |
Prompt To Fix All With AI
### Issue 1
packages/browseros-agent/apps/server/src/agent/chat-error.ts:76-80
**Unsupported credentials remain exposed**
If an upstream diagnostic contains a credential such as `Authorization: Basic <base64>` or `Cookie: session=<opaque value>`, this finite regex scrubber leaves it unchanged before the response body is displayed and copied from the Full error block, exposing the credential to the user and any pasted bug report.
**How this was verified:** The complete redaction list has no Basic-auth or cookie-value rule, while `responseBody` and `data.raw` flow through it into the rendered and copied details string.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (2): Last reviewed commit: "fix(chat): broaden secret scrubbing for ..." | Re-trigger Greptile
Forwarding the whole response body means more credential shapes can appear than the four key patterns caught. Redact value-shaped secrets on the raw text so it works whether or not the body parses as an object: JWTs, PEM private keys, Google/Slack/GitLab/GitHub tokens, URL userinfo credentials (host kept), and sensitive JSON string values matched by key name. Skewed toward over-redaction since a missed token is copied into a bug report.
Problem
Chat uses the Vercel AI SDK
useChat. When a turn fails, the classifier built the error envelope'sdetailsfrom the short constructederror.messageand capped it at 500 characters. The gateway's real, actionable reason (OpenRouter-style gateways wrap it in the response body /metadata.raw) was captured at the fetch boundary asAPICallError.responseBody/data.rawbut never forwarded, so the user saw only a generic sentence. Errors the AI SDK could not classify carried nodetailsat all, leaving nothing to copy or act on.Team decision: any error that cannot be fully expressed through the AI SDK's masked stream should be shown verbatim, after scrubbing secrets, so users are empowered to fix it.
Change
data.raw), pretty-printed as JSON, intodetailson every path, redacted and bounded at a larger cap so the whole body survives to be copied. Errors that fall through to the generic path now carry the scrubbed raw message too.Full errorheading with a copy control. The copy targets the full string, not the DOM, so the entire error is copied even when the block clips it to a scroll area on screen. The classified title and message (credits exhausted, session expired, rate limited, and so on) stay on top, unchanged.No wire or schema change: this reuses the envelope's existing
detailsfield. The side panel and new tab share the card component, so both surfaces are covered.Tests
details; falls back todata.raw; secrets redacted and a pathological body bounded; a plain error surfaces its scrubbed message as copyable detail.Follow-up
If credit exhaustion is ever returned as HTTP 200 with the error embedded in the SSE stream (rather than a 4xx the custom fetch already intercepts), the stream body would need inspecting too. The current 4xx handling covers the HTTP-error path, so that is a separate, verifiable follow-up.