vercel/next.js · error · Error

Cannot prefetch '${href}' because it cannot be converted to

Error message

Cannot prefetch '${href}' because it cannot be converted to a URL.

What it means

`createPrefetchURL` throws when the href cannot be parsed into a valid URL via `new URL(addBasePath(href), window.location.href)`. This runs during prefetch attempts (including automatic prefetch from `<Link>` with prefetch enabled). Malformed hrefs, invalid URL characters, or non-string values cause the URL constructor to throw, which is caught and re-thrown with this message.

Source

Thrown at packages/next/src/client/components/app-router-utils.ts:27

 * Given a link href, constructs the URL that should be prefetched. Returns null
 * in cases where prefetching should be disabled, like external URLs, or
 * during development.
 * @param href The href passed to <Link>, router.prefetch(), or similar
 * @returns A URL object to prefetch, or null if prefetching should be disabled
 */
export function createPrefetchURL(href: string): URL | null {
  // Don't prefetch for bots as they don't navigate.
  if (isBot(window.navigator.userAgent)) {
    return null
  }

  let url: URL
  try {
    url = new URL(addBasePath(href), window.location.href)
  } catch (_) {
    // TODO: Does this need to throw or can we just console.error instead? Does
    // anyone rely on this throwing? (Seems unlikely.)
    throw new Error(
      `Cannot prefetch '${href}' because it cannot be converted to a URL.`
    )
  }

  // Don't prefetch during development (improves compilation performance)
  if (process.env.NODE_ENV === 'development') {
    return null
  }

  // External urls can't be prefetched in the same way.
  if (isExternalURL(url)) {
    return null
  }

  return url
}

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Validate the href with `new URL(addBasePath(href), location.href)` in a try/catch before passing to Link or prefetch.
  2. URL-encode query parameter values with `encodeURIComponent`.
  3. Ensure the href is a well-formed absolute URL or root-relative path.
  4. Add a type guard to filter out invalid hrefs from dynamic data.

Example fix

// before — throws if href has invalid characters
<Link href={`/search?q=${userInput}`}>Search</Link>

// after — encode the dynamic segment
<Link href={`/search?q=${encodeURIComponent(userInput)}`}>Search</Link>
// or validate before use:
function isValidHref(h: string) {
  try { new URL(h, window.location.href); return true } catch { return false }
}
Defensive patterns

Strategy: validation

Validate before calling

import { addBasePath } from 'next/dist/client/add-base-path'
function isValidPrefetchHref(href: string): boolean {
  try {
    new URL(addBasePath(href), window.location.href)
    return true
  } catch {
    return false
  }
}

Type guard

function isParsableUrl(href: string, base?: string): href is string {
  try {
    new URL(href, base ?? window.location.href)
    return true
  } catch {
    return false
  }
}

Try / catch

try {
  router.prefetch(href)
} catch (e) {
  if (e.message.includes('cannot be converted to a URL')) {
    console.warn('Skipping invalid prefetch href:', href)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Passing an invalid href to `<Link>`, `router.prefetch()`, or any code path that triggers prefetching where the href fails URL constructor parsing — unencoded special characters, spaces, or a completely malformed string.

Common situations: Dynamic href with spaces or special characters not URL-encoded; href that is undefined/null/empty coerced to a string; relative paths with invalid syntax; query params with unencoded characters breaking the URL parser.

Related errors


AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06). Data as JSON: /api/errors/d821354455e91f15. Report an issue: GitHub.