Skip to content

Turbopack dev: imported binding left unrewritten (ReferenceError) when referenced after a for...of whose body ends in an unconditional return #95494

Description

@Onebu

Link to the code that reproduces this issue

https://github.com/Onebu/turbopack-forof-return-repro

To Reproduce

  1. pnpm install && pnpm dev (next 15.4.11, next dev --turbopack)
  2. curl http://localhost:3000/api/repro

The route imports a named function and references it after a for...of loop whose body ends in an unconditional return:

import { beta } from '../../../lib/util'

export function GET(request: Request): Response {
  const url = new URL(request.url)
  const items: string[] = url.searchParams.get('items')?.split(',') ?? []
  for (const item of items) {
    if (item === 'ok') continue
    return Response.json({ bad: item })
  }
  return Response.json({ typeofBinding: typeof beta })
}

Current vs. Expected behavior

Current (15.4.11): response is {"typeofBinding":"undefined"}. In the compiled dev chunk (.next/server/chunks/*), references to the imported binding placed after the loop are left as the bare identifier instead of being rewritten to __TURBOPACK__imported__module__...["beta"] — so calling it throws ReferenceError: beta is not defined at runtime. References before the loop in the same function, and all references in sibling functions, are rewritten correctly.

Expected: {"typeofBinding":"function"} — which is what next 16.2.10 returns for the identical app, and what webpack dev/build produce on 15.4.11.

Notes from a larger app where we hit this in production code:

  • Deterministic, survives recompiles/HMR; not a race.
  • Positional, not name-based: aliasing the import (import { beta as b }) just moves the ReferenceError to the new name; importing directly from the source module instead of a barrel doesn't help either.
  • The trigger is precisely the loop body being unable to complete normally via an unconditional tail return: making that return conditional (if (item !== '') return ...) fixes resolution; deleting it fixes it as well. Template literals, ?./??, and outer-scope writes near the loop were all ruled out.
  • Real-world impact in our app: a route handler's server-side validation call after such a loop threw ReferenceError, was swallowed by a fail-open catch, and validation was silently skipped in dev only.

Since 16.x is fixed, this is presumably already known internally or fixed incidentally — filing mainly in case a 15.x backport is considered, and for searchability (the symptom is very hard to attribute: the ReferenceError carries the original identifier name and looks like a scoping bug in user code).

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin 25.5.0
  Available memory (MB): 32768
  Available CPU cores: 10
Binaries:
  Node: 22.22.2
  npm: 10.9.7
  pnpm: 10.33.0
Relevant Packages:
  next: 15.4.11
  react: 19.2.7
  react-dom: 19.2.7
  typescript: 6.0.3
Next.js Config:
  output: N/A

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

Turbopack

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

next dev (local)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions