Skip to content

Unhandled promise rejections when navigations are superseded/aborted #210

Description

@uhyo

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions