Skip to content

Shared useApiResource hook has no stale-response guard - reachable via pagination and free-text input #7785

Description

@JSONbored

Context

useApiResource (apps/loopover-ui/src/lib/api/use-api-resource.ts:37-70) has no cancellation guard on its path dependency changing:

const load = useCallback(async () => { ...; setState(...) }, [enabled, label, path, token]);
useEffect(() => { ...; void load(); }, [enabled, load]);

Two concretely reachable sites:

  • apps/loopover-ui/src/components/site/dead-letter-queue-panel.tsx:44-46 - path = useMemo(() => buildDeadLetterQueuePath(offset), [offset]), and offset changes on every Next/Previous pagination click. Rapid clicking can let an older page's response resolve after a newer one, silently showing the wrong page's rows while the "Showing X-Y of Z" text and Prev/Next button states reflect the new offset.
  • apps/loopover-ui/src/components/site/app-panels/owner-panel.tsx:44-54 - repo is a free-text <input> (value={repo} / onChange={(e) => setRepo(e.target.value)} at lines 94-95) feeding directly into useApiResource's path, same keystroke-race class as the companion 4-panel issue.
  • apps/loopover-ui/src/routes/app.analytics.tsx:177-180 - operatorDashboardPath(selectedWindow) changes on time-window selection; lower risk (discrete clicks, not keystrokes) but same root cause.

Since this is the shared hook (also used by notification-readiness-card.tsx, digest-panel.tsx, maintainer-panel.tsx, miner-panel.tsx, commands-panel.tsx, app.operator.tsx, app.index.tsx, app.runs.tsx), fixing it once resolves this bug class for every current and future consumer of this hook.

Requirements

⚠️ Required pattern - apply the same cancelled-flag idiom already used in apps/loopover-miner-ui/src/lib/use-polled-fetch.ts:44,53,64 (set in the effect cleanup, checked before every setState) to useApiResource's load/effect. This is a distinct fix location from the companion "4 repo-picker panels" issue - those components don't use this hook, they have their own inline load functions.

Deliverables

  • useApiResource (apps/loopover-ui/src/lib/api/use-api-resource.ts) ignores a response that resolves after path (or enabled/token) changed again, using the same guard idiom as use-polled-fetch.ts.
  • A regression test simulating rapid pagination (or a rapid path change) with out-of-order response resolution, asserting the final state matches the latest request, not an earlier one.

Test Coverage Requirements

apps/loopover-ui is not covered by the src/** 99% patch gate - the new regression test is this issue's own coverage deliverable.

Expected Outcome

Rapid pagination clicks or path changes on any useApiResource consumer never result in a stale, out-of-order response silently overwriting the current state.

Links & Resources

apps/loopover-ui/src/lib/api/use-api-resource.ts:37-70, apps/loopover-miner-ui/src/lib/use-polled-fetch.ts:44,53,64 (the pattern to reuse), dead-letter-queue-panel.tsx:44-46 and owner-panel.tsx:44-54 (concrete reachable sites)

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions