Skip to content

fix(app-router): router.prefetch throws on invalid URL#1564

Open
james-elicx wants to merge 1 commit into
mainfrom
fix/router-prefetch-invalid-url
Open

fix(app-router): router.prefetch throws on invalid URL#1564
james-elicx wants to merge 1 commit into
mainfrom
fix/router-prefetch-invalid-url

Conversation

@james-elicx
Copy link
Copy Markdown
Member

Closes #1553

Summary

  • router.prefetch now throws the canonical Next.js Cannot prefetch '<href>' because it cannot be converted to a URL. message when given an unparseable URL, matching createPrefetchURL in packages/next/src/client/components/app-router-utils.ts.
  • Previously the unparseable href was swallowed inside the async prefetch IIFE and reported as console.error("[vinext] RSC prefetch setup error:", error), so the surrounding App Router error boundary never rendered (Next.js test app <h1>A prefetch threw an error</h1> never appeared).

Test plan

  • New unit test tests/router-prefetch-invalid-url.test.ts asserts throw with the canonical message for INVALID_URL = "///" and that normal hrefs still succeed.
  • vp check passes (format + lint + typecheck on 649 files).
  • Existing tests/router-javascript-urls.test.ts and tests/prefetch-cache.test.ts still pass.

Match Next.js's createPrefetchURL behaviour: when the href cannot be
parsed via `new URL(href, window.location.href)`, throw the canonical
message `Cannot prefetch '<href>' because it cannot be converted to a
URL.` so the surrounding error boundary in the App Router can surface
the failure instead of silently swallowing it inside the async prefetch
IIFE.

Closes #1553
@james-elicx
Copy link
Copy Markdown
Member Author

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 22, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1564

commit: 14ac8c9

@github-actions
Copy link
Copy Markdown

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
web preview production

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks correct and well-tested. The fix mirrors createPrefetchURL from Next.js's packages/next/src/client/components/app-router-utils.ts:22-29 — same try/catch around new URL(href, window.location.href), same canonical error message.

Verified against Next.js source:

  • Next.js test test/e2e/app-dir/app-prefetch/prefetching.test.ts:469-478 expects router.prefetch with an invalid URL to throw and surface via error boundary — the PR matches this behavior.
  • Error message Cannot prefetch '<href>' because it cannot be converted to a URL. matches exactly.
  • Placement is correct: the validation runs synchronously before the async IIFE, so the throw propagates to React's error boundary instead of being swallowed by the .catch() on line 1565.

One minor observation (not blocking): Next.js's createPrefetchURL validates new URL(addBasePath(href), ...) rather than new URL(href, ...). When basePath is non-empty, this could theoretically make a broken-looking URL parseable (e.g. new URL("/app///", origin) succeeds while new URL("///", origin) throws). This is an extremely unlikely edge case and the current implementation is correct for the default (empty basePath) and all realistic inputs. Not worth holding the PR for.

Test coverage is solid — the three cases (invalid URL throws, normal pathname works, absolute http URL works) cover the important scenarios well.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 22, 2026

Review posted and approved on PR #1564.

Summary: The fix is correct and well-aligned with Next.js behavior. The key change is a synchronous URL validation (new URL(href, window.location.href)) added to router.prefetch() before the async IIFE, so invalid URLs throw immediately — matching Next.js's createPrefetchURL — instead of being silently swallowed by the .catch() handler. The test coverage is solid with three cases covering the error path and happy paths. I noted one minor non-blocking observation about addBasePath ordering but approved since it doesn't affect realistic usage.

github run

@james-elicx james-elicx marked this pull request as ready for review May 22, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App Router: invalid URL passed to router.prefetch does not throw

1 participant