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

#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>
This commit is contained in:
cliffhall 2026-08-17 20:40:23 -04:00
commit de60983ccc

View file

@ -1031,18 +1031,21 @@ function App() {
tools: pagedTools,
nextCursor: pagedToolsCursor,
pageCount: pagedToolsPageCount,
error: pagedToolsLoadError,
loadPage: loadToolsPage,
} = usePagedTools(inspectorClient, pagedToolsState);
const {
prompts: pagedPrompts,
nextCursor: pagedPromptsCursor,
pageCount: pagedPromptsPageCount,
error: pagedPromptsLoadError,
loadPage: loadPromptsPage,
} = usePagedPrompts(inspectorClient, pagedPromptsState);
const {
resources: pagedResources,
nextCursor: pagedResourcesCursor,
pageCount: pagedResourcesPageCount,
error: pagedResourcesLoadError,
loadPage: loadResourcesPage,
} = usePagedResources(inspectorClient, pagedResourcesState);
// The active server's persisted paginated setting drives the display mode.
@ -1073,9 +1076,11 @@ function App() {
paginated: paginatedLists,
managedItems: managedTools,
managedRefresh: refreshTools,
managedError: toolsLoadError,
pagedItems: pagedTools,
pagedNextCursor: pagedToolsCursor,
pagedPageCount: pagedToolsPageCount,
pagedError: pagedToolsLoadError,
loadPage: loadToolsPage,
});
const promptsPagination = usePaginatedList({
@ -1083,9 +1088,11 @@ function App() {
paginated: paginatedLists,
managedItems: managedPrompts,
managedRefresh: refreshPrompts,
managedError: promptsLoadError,
pagedItems: pagedPrompts,
pagedNextCursor: pagedPromptsCursor,
pagedPageCount: pagedPromptsPageCount,
pagedError: pagedPromptsLoadError,
loadPage: loadPromptsPage,
});
const resourcesPagination = usePaginatedList({
@ -1093,9 +1100,11 @@ function App() {
paginated: paginatedLists,
managedItems: managedResources,
managedRefresh: refreshResources,
managedError: resourcesLoadError,
pagedItems: pagedResources,
pagedNextCursor: pagedResourcesCursor,
pagedPageCount: pagedResourcesPageCount,
pagedError: pagedResourcesLoadError,
loadPage: loadResourcesPage,
});
const tools = toolsPagination.items;
@ -4407,9 +4416,11 @@ function App() {
toolsListChanged={toolsListChanged}
promptsListChanged={promptsListChanged}
resourcesListChanged={resourcesListChanged}
toolsLoadError={toolsLoadError}
promptsLoadError={promptsLoadError}
resourcesLoadError={resourcesLoadError ?? resourceTemplatesLoadError}
toolsLoadError={toolsPagination.error}
promptsLoadError={promptsPagination.error}
resourcesLoadError={
resourcesPagination.error ?? resourceTemplatesLoadError
}
subscriptions={subscriptions}
subscriptionStreamState={subscriptionStreamState}
logs={logs}