Skip to content

fix(agent): clear stale chat error when switching provider or resetting#1001

Open
Aarjav Jain (A2rjav) wants to merge 1 commit into
browseros-ai:devfrom
A2rjav:fix/862-clear-error-on-provider-switch
Open

fix(agent): clear stale chat error when switching provider or resetting#1001
Aarjav Jain (A2rjav) wants to merge 1 commit into
browseros-ai:devfrom
A2rjav:fix/862-clear-error-on-provider-switch

Conversation

@A2rjav
Copy link
Copy Markdown

Summary

Closes #862

When a chat fails (credits exhausted, daily limit, invalid auth, malformed JSON), useChat from @ai-sdk/react keeps its error populated until a subsequent request succeeds, and exposes no public clearError. The banner therefore persists across provider/model switches and even after the user clicks "Reset conversation" — so users can't recover within the same session and end up restarting the browser.

This PR adds a small, surgical suppression layer that hides the stale error after a recovery action while still letting genuinely new errors through.

Approach

  1. New chat-error-suppression.ts module with two pure helpers:
    • computeVisibleChatError — hides chatError while its reference matches a recorded marker.
    • isStaleErrorMarkerStillCurrent — gates the effect that drops the marker as soon as useChat produces a different error reference or clears it on its own.
  2. useChatSession.ts captures the marker in two recovery paths:
    • handleSelectProvider on actual provider change, independent of message count so first-send auth failures (which produce zero assistant turns) are also covered.
    • resetConversation on the explicit clear-chat action.
  3. The returned chatError is filtered through computeVisibleChatError, so Chat.tsx and ChatError.tsx need no changes.

Why reference equality rather than tracking which provider the error belonged to? useChat allocates a fresh Error for each failed request, so the next failure on the new provider has a different reference and falls through the suppression naturally. Cleaner than juggling provider IDs and immune to AI SDK internals.

Changes

  • apps/agent/entrypoints/sidepanel/index/chat-error-suppression.ts — new pure-function module (32 lines incl. doc comment).
  • apps/agent/entrypoints/sidepanel/index/chat-error-suppression.test.ts — 9 unit tests covering: no error, no marker, matching reference, different reference (fresh error appears), structurally identical but distinct Error instances, and every marker-currency transition.
  • apps/agent/entrypoints/sidepanel/index/useChatSession.ts — wires the helpers in; the visible-error filter, the dependency-driven effect that auto-drops the marker, and the two recovery-path marker captures.

Test plan

  • bun test apps/agent/entrypoints/sidepanel/index/chat-error-suppression.test.ts — 9/9 pass.
  • bun test apps/agent/entrypoints/sidepanel/index/ — 21/21 pass (no regressions in adjacent files).
  • bunx @biomejs/biome check on the three touched files — clean.
  • Trigger any of the four documented error states (e.g. Credits exhausted), switch to another provider/model from the picker → banner clears and the new send is allowed.
  • Trigger an error on the very first send (auth failure before any assistant message exists), switch provider → banner clears.
  • Trigger an error, click "Reset conversation" → banner clears, fresh session starts clean.
  • Trigger an error, retry on the same provider, get a fresh failure → new banner shows (different Error reference is not suppressed).
  • Trigger an error, retry on the same provider and succeed → banner clears normally.

Closes browseros-ai#862

useChat from @ai-sdk/react keeps its `error` populated until a subsequent
request succeeds and exposes no public clearError. After a failure (credits
exhausted, daily limit, invalid auth, JSON parse) the banner persisted
through provider/model switches and reset clicks, blocking recovery within
the same session.

- New chat-error-suppression module:
  - computeVisibleChatError hides chatError while its reference matches a
    recorded "stale" marker
  - isStaleErrorMarkerStillCurrent gates a one-shot effect that drops the
    marker as soon as useChat produces a different error reference or
    clears it on its own
- useChatSession records the marker in two recovery paths:
  - handleSelectProvider on actual provider change (independent of message
    count, so first-send auth failures with zero assistant turns are also
    covered)
  - resetConversation on the explicit clear-chat action
- Returned chatError is filtered through computeVisibleChatError so the
  Chat UI never re-renders the suppressed banner
- 9 unit tests covering: empty state, no marker, matching reference,
  different reference (new error appears), structurally identical but
  distinct Error instances, and every marker-currency transition
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 12, 2026

PR author is not in the allowed authors list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error state persists after switching models/providers in the same session

1 participant