Skip to content

Malformed percent-encoding in an App Router dynamic segment returns 500 instead of 400 #99347

Description

@y-pakorn

Link to the code that reproduces this issue

https://github.com/y-pakorn/next-malformed-param-500

To Reproduce

  1. Clone the reproduction, then run npm install, npm run build and npm start.
  2. Request the dynamic route with a malformed percent-encoded segment:
curl -o /dev/null -w '%{http_code}\n' http://localhost:3000/items/%E0%A4
curl -o /dev/null -w '%{http_code}\n' http://localhost:3000/items/100%

The app has one dynamic route, app/items/[slug]/page.tsx, and no pages directory.

Current vs. Expected behavior

Both requests return 500 Internal Server Error with a plain text body, and nothing is logged.

I expected 400 Bad Request. next dev returns 400 for the same URLs, and so does next start once the project has any Pages Router route. A valid slug such as /items/hello returns 200, and a malformed static path such as /%E0%A4 returns 404, so only dynamic segments are affected.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.5.0: Mon Apr 27 20:41:12 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6050
  Available memory (MB): 24576
  Available CPU cores: 15
Binaries:
  Node: 26.3.0
  npm: 11.16.0
  Yarn: N/A
  pnpm: 12.4.1
Relevant Packages:
  next: 16.4.0-canary.51 // Latest available version is detected (16.4.0-canary.51).
  eslint-config-next: N/A
  react: 19.3.0
  react-dom: 19.3.0
  typescript: 7.0.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Dynamic Routes, Error Handling

Which stage(s) are affected? (Select all that apply)

next start (local), Other (Deployed)

Additional context

This also reproduces on 16.3.6 and 16.3.5. We hit it in production on a self-hosted output: "standalone" deployment running 16.3.5.

For these URLs, getRouteMatcher throws DecodeError('failed to decode param'), error code E528. base-server catches DecodeError and renders /_error with status 400, and router-server maps it to a 400 as well. An App Router-only build has no _error page, though. Its .next/server/pages directory contains only 404.html and 500.html. After adding one Pages Router route, the build includes _error.js and the same requests return 400. My guess is that the 400 response depends on the Pages Router error page and ends in a bare 500 when the build doesn't include one.

This looks like a regression of #43303. The test added for that issue in test/integration/dynamic-routing only covers the Pages Router.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Dynamic RoutesRelated to dynamic routes.Error HandlingRelated to handling errors (e.g., error.tsx, global-error.tsx).

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions