Skip to content

fix: surface list-fetch failures in paginated mode (#1998) - #2047

Merged
cliffhall merged 1 commit into
v2/mainfrom
v2/fix/1998-paginated-list-errors
Aug 18, 2026
Merged

fix: surface list-fetch failures in paginated mode (#1998)#2047
cliffhall merged 1 commit into
v2/mainfrom
v2/fix/1998-paginated-list-errors

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #1998

With Fetch Lists One Page at a Time on, a failing tools/list / prompts/list / resources/list showed an empty sidebar with no alert and no Retry — the exact behavior #1954 set out to remove — and the connect-time load left an unhandled rejection.

The cause is that #1954 wired the error off the aggregate stores only. In paginated mode those stores deliberately skip their all-page walk (deferWhenPaginated), so their error is permanently null; the paged stores that actually drive the sidebar had no error state at all, and loadPage() had a try/finally with no catch.

What changed

  1. The three paged stores get observable error state (core/mcp/state/paged{Tools,Prompts,Resources}State.ts): an errorChange event plus getError(), set in a new catch in loadPage, cleared on the next successful load and on disconnect. The rejection is still re-thrown, matching ManagedListState — the state drives the panel, while the rejection is what a caller's auth-recovery wrapper keys off to detect a 401.
  2. The connect-time load no longer floats its rejection: void this.loadPage(undefined) became void this.loadPage(undefined).catch(() => {}). Not a swallow — loadPage has already recorded the failure, and the panel renders it. (This is the same shape ManagedListState's connect handler already used, and it keeps Enable @typescript-eslint/no-floating-promises so an unhandled rejection can't fail the gate again #1959's no-floating-promises honest.)
  3. usePaginatedList selects the error by mode, the same way it already selects items: error: paginated ? pagedError : managedError. App.tsx now passes toolsPagination.error / promptsPagination.error / resourcesPagination.error instead of always reading the managed stores.
  4. useManagedListErroruseListError (core/react/useListError.ts). Both store families expose the identical errorChange: Error | null, so the hook now serves both and no longer imports ManagedListEventMap — it declares the one-event contract it actually needs. The four useManaged* hooks and the three usePaged* hooks share it.

Retry already does the right thing in paginated mode: onRefreshTools routes to usePaginatedList.onRefresh, which reloads page 1 from the paged store.

Screenshots

Paginated mode against a server whose tools/list rejects with -32603.

Before — empty panel, no alert, no Retry (and an unhandled rejection from the connect-time load):

Tools tab in paginated mode before the fix: empty panel, no alert

After — the same alert + Retry the aggregate path has shown since #1954:

Tools tab in paginated mode after the fix: "Couldn't load tools" alert with the server's error and a Retry button

Tests

  • paged{Tools,Prompts,Resources}State.test.ts: records + dispatches + re-throws, wraps a non-Error rejection, clears on the next success, clears on disconnect, and records a connect-time auto-load failure rather than floating it.
  • usePaged{Tools,Prompts,Resources}.test.tsx: the hook surfaces and clears the store's error, and reports null for a null store.
  • usePaginatedList.test.tsx: reports the managed error in all-pages mode and the paged error in paginated mode.

Deliberately out of scope

ManagedListState.refresh also calls markResponseRejected for an SDK decode rejection, so the Protocol entry stops rendering a rejected result as a clean success. The paged stores don't, and this PR doesn't add it — that is #1953's surface, and the issue's suggested shape doesn't ask for it. Worth a follow-up issue if we want the two paths identical there.

npm run ci passes locally.

#1954 wired the list-load error off the aggregate managed stores only.
With `paginatedLists` on, those stores deliberately skip their all-page
walk, so their error is permanently null — while the paged stores that
actually drive the sidebar had no error state at all and `loadPage` had
a try/finally with no catch. A failing list showed an empty panel with
no alert and no Retry, and the connect-time `void loadPage(undefined)`
left an unhandled rejection.

- Give PagedTools/Prompts/ResourcesState observable error state: an
  `errorChange` event plus `getError()`, set in a new catch in
  `loadPage`, cleared on the next success and on disconnect. The
  rejection is still re-thrown, matching ManagedListState — callers'
  auth-recovery wrappers key off it to detect a 401.
- Catch the connect-time load's rejection (`.catch(() => {})`) now that
  the failure is recorded as state rather than lost.
- Select the error by mode in `usePaginatedList`, the same way `items`
  already is, and read it in App.tsx from the pagination model.
- Rename `useManagedListError` to `useListError`: both store families
  expose an identical `errorChange`, so the hook declares that one-event
  contract itself instead of importing ManagedListEventMap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Aug 18, 2026
@cliffhall
cliffhall requested a balanced review from Copilot August 18, 2026 00:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes #1998 by surfacing paginated list-fetch failures in the existing alert and Retry UI.

Changes:

  • Adds observable error state to paged tools, prompts, and resources stores.
  • Selects managed or paged errors based on pagination mode.
  • Prevents connect-time load failures from becoming unhandled rejections and adds comprehensive tests.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated no comments.

Show a summary per file
File Description
core/react/usePagedTools.ts Exposes paged tools errors.
core/react/usePagedResources.ts Exposes paged resources errors.
core/react/usePagedPrompts.ts Exposes paged prompts errors.
core/react/useManagedTools.ts Uses the generalized error hook.
core/react/useManagedResourceTemplates.ts Uses the generalized error hook.
core/react/useManagedResources.ts Uses the generalized error hook.
core/react/useManagedPrompts.ts Uses the generalized error hook.
core/react/useListError.ts Generalizes error subscriptions across list-store families.
core/mcp/state/pagedToolsState.ts Records, emits, clears, and rethrows tools errors.
core/mcp/state/pagedResourcesState.ts Records, emits, clears, and rethrows resources errors.
core/mcp/state/pagedPromptsState.ts Records, emits, clears, and rethrows prompts errors.
clients/web/src/test/core/react/usePagedTools.test.tsx Tests paged tools error propagation.
clients/web/src/test/core/react/usePagedResources.test.tsx Tests paged resources error propagation.
clients/web/src/test/core/react/usePagedPrompts.test.tsx Tests paged prompts error propagation.
clients/web/src/test/core/react/useListError.test.tsx Updates generalized error-hook coverage.
clients/web/src/test/core/mcp/state/pagedToolsState.test.ts Tests tools error lifecycle and connect failures.
clients/web/src/test/core/mcp/state/pagedResourcesState.test.ts Tests resources error lifecycle and connect failures.
clients/web/src/test/core/mcp/state/pagedPromptsState.test.ts Tests prompts error lifecycle and connect failures.
clients/web/src/hooks/usePaginatedList.ts Selects errors from the active list source.
clients/web/src/hooks/usePaginatedList.test.tsx Tests mode-aware error selection.
clients/web/src/App.tsx Routes active paginated errors into list panels.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@cliffhall
cliffhall merged commit 7be5875 into v2/main Aug 18, 2026
3 of 4 checks passed
@cliffhall
cliffhall deleted the v2/fix/1998-paginated-list-errors branch August 18, 2026 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Paginated mode swallows list-fetch failures: #1954's error UI never fires, and the connect-time load can leave an unhandled rejection

2 participants