Found during a library audit (see AUDIT.md on branch claude/library-audit-sxpugz, §1.9). Code-level finding against v1.1.1 — not verified empirically (the test mock's navigate() always resolves), so first step is a repro/regression test with a rejecting finished promise.
Problem
navigation.navigate().finished rejects (typically AbortError) when a navigation is interrupted by a newer one, or prevented. Several fire-and-forget paths discard that rejection:
NavigationAPIAdapter.navigate() ignores the returned { committed, finished } entirely (packages/router/src/core/NavigationAPIAdapter.ts).
setSearchParams does void navigateAsync(...) (packages/router/src/hooks/useSearchParams.ts) — rapid successive calls (e.g. typing in a search box that syncs to the URL) abort each other.
- User code calling the async
setState without await/.catch hits the same via navigateAsync.
Expected result: console noise from unhandledrejection events, and potentially error-tracking alerts in production apps.
Suggested fix
Attach a no-op .catch on internal fire-and-forget paths (abort of a superseded navigation is expected behavior, not an error), while still propagating rejection to callers who explicitly await navigateAsync. The mock in src/__tests__/setup.ts could gain a way to make finished reject so this path is testable.
Found during a library audit (see
AUDIT.mdon branchclaude/library-audit-sxpugz, §1.9). Code-level finding against v1.1.1 — not verified empirically (the test mock'snavigate()always resolves), so first step is a repro/regression test with a rejectingfinishedpromise.Problem
navigation.navigate().finishedrejects (typicallyAbortError) when a navigation is interrupted by a newer one, or prevented. Several fire-and-forget paths discard that rejection:NavigationAPIAdapter.navigate()ignores the returned{ committed, finished }entirely (packages/router/src/core/NavigationAPIAdapter.ts).setSearchParamsdoesvoid navigateAsync(...)(packages/router/src/hooks/useSearchParams.ts) — rapid successive calls (e.g. typing in a search box that syncs to the URL) abort each other.setStatewithoutawait/.catchhits the same vianavigateAsync.Expected result: console noise from
unhandledrejectionevents, and potentially error-tracking alerts in production apps.Suggested fix
Attach a no-op
.catchon internal fire-and-forget paths (abort of a superseded navigation is expected behavior, not an error), while still propagating rejection to callers who explicitly awaitnavigateAsync. The mock insrc/__tests__/setup.tscould gain a way to makefinishedreject so this path is testable.