next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Bug Report: `params.slug` is empty object `{}` in Edge Runtime with catch-all optional routes `[[...slug]]`

Open ahmedbzubairu opened this issue 1 month ago • 4 comments

Link to the code that reproduces this issue

https://codesandbox.io/p/sandbox/hungry-solomon-2dd9xw

To Reproduce

  1. Create a catch-all optional route file: app/test/[[...slug]]/page.tsx
  2. Set the route to use edge runtime: export const runtime = "edge"
  3. Type params as Promise<{ slug?: string[] }> (Next.js 15 format)
  4. Await the params Promise: const resolvedParams = await params
  5. Access resolvedParams.slug - it will be undefined
  6. Inspect resolvedParams - it will be an empty object {}

Example URL: /test/homepage

Expected: resolvedParams should be { slug: ['homepage'] } Actual: resolvedParams is {}

Current vs. Expected behavior

Current Behavior

  • params is correctly typed as Promise<{ slug?: string[] }>
  • await params resolves successfully (no error thrown)
  • The resolved value is an empty object {} with no properties
  • resolvedParams.slug is undefined
  • Object.keys(resolvedParams) returns []
  • This only occurs in Edge Runtime - works correctly in Node.js runtime

Expected Behavior

  • await params should resolve to { slug: ['homepage'] } for URL /test/homepage
  • resolvedParams.slug should be ['homepage'] (array of path segments)
  • For root route /test, resolvedParams.slug should be undefined (optional catch-all)

Provide environment information

- **Next.js version:** `15.5.7`
- **React version:** `19.0.1`
- **Node.js version:** `>=18.18.0`
- **Runtime:** Edge Runtime (`export const runtime = "edge"`)
- **Deployment:** Cloudflare Pages (requires edge runtime)
- **Route Type:** Catch-all optional route `[[...slug]]`
- **App Router:** Yes (using `app/` directory)

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

Dynamic Routes

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

Other (Deployed)

Additional context

Workaround Currently Used

We're using middleware to extract the slug from the URL pathname and pass it via headers:

// middleware.ts
export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  if (pathname.startsWith('/test/')) {
    const slug = pathname.replace(/^\/test\/?/, '').replace(/\/$/, '') || 'home';
    const requestHeaders = new Headers(request.headers);
    requestHeaders.set('x-pathname', pathname);
    return NextResponse.next({ request: { headers: requestHeaders } });
  }
  return NextResponse.next();
}

ahmedbzubairu avatar Dec 11 '25 15:12 ahmedbzubairu

What behavior do you observe locally though? Not local dev, but local build + start?

icyJoseph avatar Dec 11 '25 18:12 icyJoseph

@icyJoseph The issues occurs in local build + start as well. When export const runtime = "edge", it fails to get the param

ahmedbzubairu avatar Dec 11 '25 20:12 ahmedbzubairu

Could you submit a real repository please? Otherwise I am gonna be asking questions here, eventually time box time to try to repro, and likely fail to reproduce the issue.

icyJoseph avatar Dec 11 '25 20:12 icyJoseph

@icyJoseph https://codesandbox.io/p/sandbox/hungry-solomon-2dd9xw Can you check this

ahmedbzubairu avatar Dec 11 '25 21:12 ahmedbzubairu

Hi @icyJoseph , any update on this

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!

newtronahmed avatar Dec 12 '25 12:12 newtronahmed