vercel/next.js · error · DecodeError

failed to decode param

Error message

failed to decode param

What it means

Thrown as a DecodeError when decodeURIComponent fails on any path segment of a requested page bundle. The webpack hot reloader splits the bundle URL into params and decodeURIComponent-maps each segment; a malformed percent-encoding sequence (e.g. a lone '%' or incomplete multibyte escape) makes decodeURIComponent throw, which is converted into DecodeError('failed to decode param'). This breaks serving that particular page bundle in dev.

Source

Thrown at packages/next/src/server/dev/hot-reloader-webpack.ts:384

    // and then the bundle will be served like usual by the actual route in server/index.js
    const handlePageBundleRequest = async (
      pageBundleRes: ServerResponse,
      parsedPageBundleUrl: UrlObject
    ): Promise<{ finished?: true }> => {
      const { pathname } = parsedPageBundleUrl
      if (!pathname) return {}

      const params = matchNextPageBundleRequest(pathname)
      if (!params) return {}

      let decodedPagePath: string

      try {
        decodedPagePath = `/${params.path
          .map((param: string) => decodeURIComponent(param))
          .join('/')}`
      } catch (_) {
        throw new DecodeError('failed to decode param')
      }

      const page = denormalizePagePath(decodedPagePath)

      if (page === '/_error' || BLOCKED_PAGES.indexOf(page) === -1) {
        try {
          await this.ensurePage({ page, clientOnly: true, url: req.url })
        } catch (error) {
          return await renderScriptError(pageBundleRes, getProperError(error))
        }

        const errors = await this.getCompilationErrors(page)
        if (errors.length > 0) {
          return await renderScriptError(pageBundleRes, errors[0], {
            verbose: false,
          })
        }
      }

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Inspect the failing request URL (server logs) for stray '%' or truncated escape sequences and fix the client/proxy generating it.
  2. If a reverse proxy is re-encoding the path, configure it to pass URLs through unchanged.
  3. Use URL-encoding-safe page names (avoid special characters in page filenames).
  4. Reproduce with curl using the exact URL to confirm the malformed segment, then correct the source of the request.

Example fix

# before (malformed percent escape)
curl '/_next/static/dev/pages/%E0%A4.js'

# after (properly encoded path)
curl '/_next/static/dev/pages/about.js'
Defensive patterns

Strategy: try-catch

Validate before calling

// Reject malformed percent-encoding before it reaches the page-bundle handler
function isSafeEncodedPath(segments: string[]): boolean {
  for (const seg of segments) {
    try { decodeURIComponent(seg) } catch { return false }
  }
  return true
}

Try / catch

try {
  decodedPagePath = '/' + params.path.map((p: string) => decodeURIComponent(p)).join('/')
} catch {
  throw new DecodeError('failed to decode param')
}

Prevention

When it happens

Trigger: A request to /_next/static/<buildid>/pages/<segments>.js where one segment contains an invalid percent-encoded sequence such as '%zz', '%E0%A4', or a raw percent sign. decodeURIComponent throws and is caught and rethrown as DecodeError.

Common situations: A proxy/CDN rewrites or truncates URLs, producing broken percent escapes. A bot or scanner requests a malformed _next path. A route/page name contains characters that get mis-encoded by a reverse proxy. Custom middleware that rewrites the URL incorrectly.

Understand the failure class

Related errors


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