fix(router): suppress abort errors in single fetch#14761
fix(router): suppress abort errors in single fetch#14761yoni-noma wants to merge 13 commits intoremix-run:devfrom
Conversation
Normalize fetch aborts and ignore abort results during data strategy. Add integration coverage for navigation during fetcher polling.
🦋 Changeset detectedLatest commit: 6bfdf4e The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Hi @yoni-noma, Welcome, and thank you for contributing to React Router! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run. Thanks! - The Remix team |
Add changeset for abort handling fix and sign CLA.
|
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
Prefer signal reason and AbortError types; only fall back to message matching for fetch TypeError cases.
Note TypeError message matching is a browser fallback only.
|
Added a short note in code: we prefer abort signal/AbortError checks and only fall back to TypeError message matching to handle browser abort race cases. The fallback is intentionally narrow (TypeError-only) to minimize false positives. Should improve behavior for Chrome/Safari/Firefox where aborted fetches sometimes surface as TypeError. |
Extract internal isAbortError helper to avoid duplication.
Ensure abort races surface AbortError consistently and add a targeted dataStrategy interruption test to guard the regression. Co-authored-by: Cursor <cursoragent@cursor.com>
Avoid treating TypeError as abort in manifest patch fetches. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep debug logging out of upstream change set. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Writing `{ type: data, data: undefined }` for aborted route results
causes mergeLoaderData to overwrite existing valid loaderData with
undefined. This crashes hooks like useRouteLoaderData('root') that
expect data to be present (e.g. "No requestInfo found in root loader").
Instead, skip aborted routes entirely so mergeLoaderData preserves
the previous valid data. The outer catch path already does this
correctly by returning empty dataResults.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix: per-route abort suppression was wiping loaderDataWe discovered a bug in the per-route abort suppression logic introduced in this PR. When an aborted route result was detected in the Impact: Hooks like Root cause: The Fix (commit e21f7ed): Skip aborted routes entirely ( This is safe because:
|
…etcher consumers
When callDataStrategy skips a route result via abort detection (isAbortError),
downstream fetcher consumers crash because they assume results always exist:
- handleFetcherLoader: result is undefined → isErrorResult(undefined) throws
- handleFetcherAction: actionResult is undefined → isRedirectResult crashes
- revalidation fetchers: result is undefined → invariant("Did not find
corresponding fetcher result") throws
Add null guards in all three consumer paths to gracefully settle fetchers
with their previous data when results are missing due to abort detection.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix: defensive null guards for skipped abort results in fetcher consumersWe identified two production errors caused by Sentry issues
Root causeWhen a browser-level abort occurs (e.g., user navigates while fetchers are in-flight), the fetch throws a
FixAdd null guards in all three consumer paths. When a result is missing (skipped by abort detection), the fetcher gracefully settles to idle with its previous data preserved — analogous to how No changes to abort detection logic — |
… in fetcher discovery error paths
The isAbortError check in fetchAndApplyManifestPatches was catching
DOMException("AbortError") even when signal.aborted was still false.
This created an inconsistency with discoverRoutes (which checks signal.aborted),
causing manifest fetches to silently return with no routes patched while
discoverRoutes proceeded as if no abort occurred — resulting in 404 errors.
Fix:
1. Revert fetchAndApplyManifestPatches to the original signal?.aborted check
2. Add isAbortError guards in handleFetcherAction and handleFetcherLoader
discovery error paths to silently suppress abort-related discovery errors
This ensures consistency between fetchAndApplyManifestPatches and discoverRoutes,
while still gracefully handling abort-related errors in the fetcher consumers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Update: Manifest abort detection fix + fetcher discovery error guardsProblem discovered (WEB-APP-1G9)The
This was observed in CI (Playwright smoke tests) where rapid navigation creates tight abort timing windows. Changes in this update
Navigation callers (the two Summary of all changes in this PR
|
Description
Fixes navigation/fetcher abort crashes caused by inconsistent abort error shapes (AbortError/TypeError/undefined) in single-fetch + data strategy. Normalizes aborts and ensures abortPromise rejects with AbortError so aborted handlers do not bubble to error boundaries. TypeError-message fallback only applies to fetcher aborts.
Related: #14203
Changes
isAbortErrorwith a TypeError fallback only for fetcherscallDataStrategyand ignore manifest patch abortsabortPromisewith AbortError (plus dev-only debug logs)Impact
Prevents abort-related TypeError/undefined errors from reaching the error boundary while keeping real navigation failures intact.
Test plan