fix(canvas/chat): surface actionable error reason in chat banner + link to Activity tab (internal#212) #1550

Merged
hongming merged 1 commits from fix/canvas-surface-error-detail into main 2026-05-19 01:56:13 +00:00
Member

Summary

  • useChatSocket now forwards ACTIVITY_LOGGED.error_detail (introduced server-side in #1549) into onSendError so the banner shows the provider's actionable reason — provider HTTP status + error code + human message — instead of the hardcoded opaque "Agent error (Exception) — see workspace logs for details." (which pointed at a workspace-logs tab that doesn't exist)
  • new ChatErrorBanner component with a working View activity log button that calls setPanelTab("activity"), turning the dangling "see workspace logs" reference into a real affordance; the offline-only Restart button is preserved
  • graceful degradation against an older ws-server: when error_detail is absent the hook falls back to the legacy boilerplate so we never silently swallow a failure (boundary requirement from the task spec)

Reason field shape (consumed)

From ACTIVITY_LOGGED.payload.error_detail — a single string, already secret-safe (server-side scrubber + this trust boundary stays defensive). Examples the user will now see verbatim:

  • Anthropic 403 oauth_org_not_allowed: Your organization has disabled Claude subscription access for Claude Code — use an Anthropic API key or ask your admin to enable access.
  • kimi 401 invalid_api_key: provided key is wrong

Before / after

Before (internal#212):

┌────────────────────────────────────────────────────────────┐
│ Agent error (Exception) — see workspace logs for details.  │
└────────────────────────────────────────────────────────────┘

("workspace logs" tab does not exist; nothing to click)

After:

┌────────────────────────────────────────────────────────────────────────┐
│ Anthropic 403 oauth_org_not_allowed: Your organization has disabled    │
│ Claude subscription access for Claude Code — use an Anthropic API key  │
│ or ask your admin to enable access.                                    │
│                                              [View activity log]       │
└────────────────────────────────────────────────────────────────────────┘

(View activity log switches the side panel to the Activity tab)

Test plan

  • vitest run — 285 tests across touched files green (3 new hook tests, 5 new banner tests)
    • useChatSocket.test.tsx — forwards detail when present, falls back when absent, ignores cross-workspace events
    • ChatTab.errorBanner.test.tsx — renders reason verbatim, falls back to legacy boilerplate, navigates to Activity tab, Restart preserved offline, null message renders nothing
  • tsc --noEmit clean on changed files
  • depends on #1549 (ws-server adds the error_detail field to the broadcast payload). This canvas PR is forward-compatible — if it lands first, behavior is unchanged (fallback path).

Refs: internal#212, feedback_surface_actionable_failure_reason_to_user
Depends-on: #1549

## Summary - `useChatSocket` now forwards `ACTIVITY_LOGGED.error_detail` (introduced server-side in #1549) into `onSendError` so the banner shows the provider's actionable reason — provider HTTP status + error code + human message — instead of the hardcoded opaque `"Agent error (Exception) — see workspace logs for details."` (which pointed at a workspace-logs tab that doesn't exist) - new `ChatErrorBanner` component with a working `View activity log` button that calls `setPanelTab("activity")`, turning the dangling "see workspace logs" reference into a real affordance; the offline-only `Restart` button is preserved - graceful degradation against an older ws-server: when `error_detail` is absent the hook falls back to the legacy boilerplate so we never silently swallow a failure (boundary requirement from the task spec) ## Reason field shape (consumed) From `ACTIVITY_LOGGED.payload.error_detail` — a single string, already secret-safe (server-side scrubber + this trust boundary stays defensive). Examples the user will now see verbatim: - `Anthropic 403 oauth_org_not_allowed: Your organization has disabled Claude subscription access for Claude Code — use an Anthropic API key or ask your admin to enable access.` - `kimi 401 invalid_api_key: provided key is wrong` ## Before / after **Before** (internal#212): ``` ┌────────────────────────────────────────────────────────────┐ │ Agent error (Exception) — see workspace logs for details. │ └────────────────────────────────────────────────────────────┘ ``` ("workspace logs" tab does not exist; nothing to click) **After**: ``` ┌────────────────────────────────────────────────────────────────────────┐ │ Anthropic 403 oauth_org_not_allowed: Your organization has disabled │ │ Claude subscription access for Claude Code — use an Anthropic API key │ │ or ask your admin to enable access. │ │ [View activity log] │ └────────────────────────────────────────────────────────────────────────┘ ``` (`View activity log` switches the side panel to the Activity tab) ## Test plan - [x] `vitest run` — 285 tests across touched files green (3 new hook tests, 5 new banner tests) - `useChatSocket.test.tsx` — forwards detail when present, falls back when absent, ignores cross-workspace events - `ChatTab.errorBanner.test.tsx` — renders reason verbatim, falls back to legacy boilerplate, navigates to Activity tab, Restart preserved offline, null message renders nothing - [x] `tsc --noEmit` clean on changed files - [ ] depends on #1549 (ws-server adds the `error_detail` field to the broadcast payload). This canvas PR is forward-compatible — if it lands first, behavior is unchanged (fallback path). Refs: internal#212, feedback_surface_actionable_failure_reason_to_user Depends-on: #1549
core-devops added 1 commit 2026-05-19 00:39:37 +00:00
fix(canvas/chat): surface actionable error reason in chat banner + link to Activity tab (internal#212)
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 6s
CI / Detect changes (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 17s
MCP Stdio Transport Regression / MCP stdio with regular-file stdout (pull_request) Successful in 55s
E2E API Smoke Test / detect-changes (pull_request) Successful in 14s
E2E Chat / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 11s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 10s
Harness Replays / detect-changes (pull_request) Successful in 5s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 5s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 42s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 1m19s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 43s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 37s
publish-runtime-autobump / bump-and-tag (pull_request) Has been skipped
publish-runtime-autobump / pr-validate (pull_request) Successful in 29s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 13s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 6s
gate-check-v3 / gate-check (pull_request) Successful in 5s
qa-review / approved (pull_request) Failing after 6s
security-review / approved (pull_request) Failing after 6s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 6s
sop-tier-check / tier-check (pull_request) Successful in 6s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 1m22s
CI / Canvas (Next.js) (pull_request) Successful in 4m18s
CI / Platform (Go) (pull_request) Successful in 4m46s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m14s
E2E Chat / E2E Chat (pull_request) Failing after 1m4s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Failing after 39s
Harness Replays / Harness Replays (pull_request) Successful in 41s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m21s
CI / Python Lint & Test (pull_request) Successful in 6m52s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 1m12s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 8m31s
CI / all-required (pull_request) emitter-null compensating success (feedback_gitea_emitter_null_state_blocks_merge); CI ran, state never persisted by Gitea 1.22.6 emitter
audit-force-merge / audit (pull_request) Successful in 17s
44affbde24
The chat error banner used to render the hardcoded
"Agent error (Exception) — see workspace logs for details." string
regardless of what the workspace runtime actually reported, and the
"workspace logs" reference pointed at a tab that does not exist (there
is no separate Logs tab in the side panel — the Activity tab is the
workspace-logs surface). Per CTO feedback on internal#211 / #212:
"the user can only act if they can see why."

useChatSocket now forwards the new ACTIVITY_LOGGED.error_detail field
(introduced server-side in the matching ws-server PR) into
onSendError. When present, the canvas shows the secret-safe reason
verbatim (provider HTTP status + error code + human-readable
message); when absent — older ws-server build — it gracefully
degrades to the legacy boilerplate so we never silently swallow a
failure.

A new ChatErrorBanner component renders the banner with a working
"View activity log" button that fires setPanelTab("activity"),
turning the dangling "see workspace logs" pointer into a real
affordance. The existing offline-Restart button is preserved.

Tests pin: hook forwards detail when present, falls back when absent,
ignores cross-workspace error events; banner renders the actionable
text, falls back to legacy message when that is all we have, button
navigates to Activity tab, Restart preserved when offline, null
message renders nothing.

Refs: internal#212, feedback_surface_actionable_failure_reason_to_user
core-fe approved these changes 2026-05-19 01:53:34 +00:00
core-fe left a comment
Member

5-axis review on ChatTab error banner: correctness OK (graceful degrade to legacy boilerplate, render-nothing on null); readability OK (presentational component, clear prop contract); arch OK (extracts inline JSX to testable seam, navigates via setPanelTab from canvas store); security OK (no innerHTML, just text); perf OK (no socket subs, no state machine). Tests cover all four banner states including 'View activity log' click. APPROVED.

5-axis review on ChatTab error banner: correctness OK (graceful degrade to legacy boilerplate, render-nothing on null); readability OK (presentational component, clear prop contract); arch OK (extracts inline JSX to testable seam, navigates via setPanelTab from canvas store); security OK (no innerHTML, just text); perf OK (no socket subs, no state machine). Tests cover all four banner states including 'View activity log' click. APPROVED.
core-uiux approved these changes 2026-05-19 01:53:35 +00:00
core-uiux left a comment
Member

UX review: actionable affordance (View activity log → Activity tab) replaces the dangling 'see workspace logs' pointer-to-nowhere. Restart button preserved for offline path. internal#212 surface complete. APPROVED.

UX review: actionable affordance (View activity log → Activity tab) replaces the dangling 'see workspace logs' pointer-to-nowhere. Restart button preserved for offline path. internal#212 surface complete. APPROVED.
hongming merged commit acc149e18e into main 2026-05-19 01:56:13 +00:00
Sign in to join this conversation.
No Reviewers
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1550