Symptom
Create/Edit Campaign dialogs show "No world maps available" even though GET /api/maps/world returns worlds. No error toast. Refresh does not reliably fix it.
Root cause
The app runs in React StrictMode (dev double-mount). components/campaign-manager/use-world-maps.ts starts the fetch on mount 1 and aborts it on the immediate StrictMode unmount. utils/world-map-cache.ts shares one in-flight promise across callers, and that shared request is bound to the first caller's AbortSignal — so mount 2 joins the already-aborted request, receives AbortError, and the hook swallows AbortError (use-world-maps.ts:36) without retrying. Empty list, no toast, no refetch. It is a race: when the API responds before StrictMode unmounts, everything works, which is why it is intermittent (cold/slow API loses the race).
Fix
A shared cache request must not be tied to one consumer's abort signal. In utils/world-map-cache.ts:
const request = listWorldMapsFetcher({}) // was: listWorldMapsFetcher({ signal })
Consumers that unmount simply ignore the result; the cache still populates for the next opener. Optionally also: on AbortError rejection do not store it in cache.error.
Found while browser-testing the lazy-world-tiles branch (2026-08-10); pre-existing, unrelated to that branch (code last touched in the campaign-manager decomposition refactor, commit 744d4d7).
Symptom
Create/Edit Campaign dialogs show "No world maps available" even though
GET /api/maps/worldreturns worlds. No error toast. Refresh does not reliably fix it.Root cause
The app runs in React StrictMode (dev double-mount).
components/campaign-manager/use-world-maps.tsstarts the fetch on mount 1 and aborts it on the immediate StrictMode unmount.utils/world-map-cache.tsshares one in-flight promise across callers, and that shared request is bound to the first caller's AbortSignal — so mount 2 joins the already-aborted request, receives AbortError, and the hook swallows AbortError (use-world-maps.ts:36) without retrying. Empty list, no toast, no refetch. It is a race: when the API responds before StrictMode unmounts, everything works, which is why it is intermittent (cold/slow API loses the race).Fix
A shared cache request must not be tied to one consumer's abort signal. In
utils/world-map-cache.ts:Consumers that unmount simply ignore the result; the cache still populates for the next opener. Optionally also: on AbortError rejection do not store it in
cache.error.Found while browser-testing the lazy-world-tiles branch (2026-08-10); pre-existing, unrelated to that branch (code last touched in the campaign-manager decomposition refactor, commit 744d4d7).