Skip to content

fix(app): handle CancelledError from cancelled provider usage queries#1885

Open
theslava wants to merge 2 commits into
getpaseo:mainfrom
theslava:fix/context-window-tooltip-race
Open

fix(app): handle CancelledError from cancelled provider usage queries#1885
theslava wants to merge 2 commits into
getpaseo:mainfrom
theslava:fix/context-window-tooltip-race

Conversation

@theslava

@theslava theslava commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Linked issue

Closes # (bug fix — no prior issue)

Type of change

  • Bug fix
  • New feature (with prior issue + design alignment)
  • Refactor / code improvement
  • Docs

What does this PR do

Fixes a crash when the context window meter tooltip is dismissed while a provider usage query is still in flight. The CancelledError thrown by the aborted request was unhandled, causing the error boundary to fire.

Changes:

  • Catches CancelledError in the usage query handler so dismissing a tooltip doesn't propagate an error
  • Gracefully handles the race between component unmount and async fetch completion

Files changed:

  • packages/app/src/components/context-window-meter.tsx
  • packages/app/src/provider-usage/use-provider-usage.ts (+2 lines, -4 lines)

How did you verify it

  • Built with npm run build:client — passes
  • Reproduced the bug: open tooltip, dismiss before fetch completes → previously crashed, now silent
  • Typecheck (npm run typecheck) and lint (npm run lint) pass

Checklist

  • One focused change. Unrelated cleanups split out.
  • npm run typecheck passes
  • npm run lint passes
  • npm run format ran (Biome)
  • UI changes include screenshots or video for every affected platform
  • Tests added or updated where it made sense

theslava added 2 commits July 3, 2026 04:35
Move canFetch guard before first await so disabled refresh bails out synchronously instead of entering async path. Add .catch() swallow for fire-and-forget call to prevent unhandled promise rejections when react-query cancels pending queries during rapid tooltip open/close cycles.
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a crash that occurred when the context-window-meter tooltip was dismissed while a provider usage query was still in flight — the CancelledError from the aborted request was previously unhandled and triggered the error boundary.

  • context-window-meter.tsx: Adds .catch(() => {}) to the void refreshProviderUsage() call so the unhandled CancelledError no longer propagates to the error boundary when the tooltip is dismissed mid-fetch.
  • use-provider-usage.ts: Moves the canFetch guard before queryClient.invalidateQueries, avoiding a redundant cache-invalidation call when the runtime conditions for fetching are not met.

Confidence Score: 4/5

Safe to merge — the crash is fixed and there are no regressions in the query lifecycle.

The blanket .catch(() => {}) in the component works for the reported bug but silently discards any non-cancellation error that escapes refresh, making future debugging harder. React Query's own error state still surfaces those failures in the UI, so there is no data-loss risk here — it is a quality concern rather than a present defect.

packages/app/src/components/context-window-meter.tsx — the blanket catch warrants a second look if error observability is important for this path.

Important Files Changed

Filename Overview
packages/app/src/components/context-window-meter.tsx Adds .catch(() => {}) to the refreshProviderUsage() call to suppress the unhandled CancelledError. The blanket catch also silently drops non-cancellation errors, though React Query state still reflects them.
packages/app/src/provider-usage/use-provider-usage.ts Moves the canFetch guard above invalidateQueries, avoiding a redundant cache-invalidation call when conditions for fetching aren't met. Safe: when canFetch is false, enabled is also false, so the previously-called invalidateQueries would never have triggered a background refetch anyway.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant ContextWindowMeter
    participant useProviderUsage
    participant QueryClient

    User->>ContextWindowMeter: Open tooltip
    ContextWindowMeter->>useProviderUsage: refresh()
    useProviderUsage->>QueryClient: invalidateQueries(queryKey)
    useProviderUsage->>QueryClient: fetchQuery(queryKey)
    Note over QueryClient: Fetch in flight...
    User->>ContextWindowMeter: Dismiss tooltip (unmount)
    QueryClient-->>useProviderUsage: throws CancelledError
    useProviderUsage-->>ContextWindowMeter: Promise rejects
    ContextWindowMeter->>ContextWindowMeter: ".catch(() => {}) — suppressed (AFTER fix)"
    Note over ContextWindowMeter: Error boundary NOT triggered
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant ContextWindowMeter
    participant useProviderUsage
    participant QueryClient

    User->>ContextWindowMeter: Open tooltip
    ContextWindowMeter->>useProviderUsage: refresh()
    useProviderUsage->>QueryClient: invalidateQueries(queryKey)
    useProviderUsage->>QueryClient: fetchQuery(queryKey)
    Note over QueryClient: Fetch in flight...
    User->>ContextWindowMeter: Dismiss tooltip (unmount)
    QueryClient-->>useProviderUsage: throws CancelledError
    useProviderUsage-->>ContextWindowMeter: Promise rejects
    ContextWindowMeter->>ContextWindowMeter: ".catch(() => {}) — suppressed (AFTER fix)"
    Note over ContextWindowMeter: Error boundary NOT triggered
Loading

Reviews (1): Last reviewed commit: "Merge branch 'getpaseo:main' into fix/co..." | Re-trigger Greptile

Comment thread packages/app/src/components/context-window-meter.tsx
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.

1 participant