Skip to content

Turbopack build panics (EISDIR) in 16.3.0 when output tracing encounters a symlink to a directory (regression from 16.2) #96626

Description

@rollokd

Link to the code that reproduces this issue

https://github.com/rollokd/next-turbopack-nft-symlink-eisdir

To Reproduce

  1. npm install
  2. npm run build

The repro is intentionally tiny: a fresh app-router app plus

  • data/dir-link — a symlink pointing at the directory data/real-dir (committed as a symlink)
  • an outputFileTracingIncludes glob that matches it:
// next.config.ts
outputFileTracingIncludes: {
  '/*': ['./data/**/*'],
}

Control: npm install next@16.2.11 && rm -rf .next && npm run build on the same project succeeds, so this is a 16.3.0 regression.

Current vs. Expected behavior

Current: next build crashes with a fatal Turbopack panic:

Error [TurbopackInternalError]: reading file "/private/tmp/nft-symlink-repro/data/dir-link"

Caused by:
- Is a directory (os error 21)

Debug info:
- Execution of emit_all_output_assets_once_with_issues_operation failed
- Execution of EntrypointsOperation::new failed
- Execution of emit_all_output_assets_once_operation failed
- Execution of Project::emit_all_output_assets failed
- Execution of emit_assets failed
- Execution of emit failed
- Execution of <NftJsonAsset as Asset>::content failed
- Execution of *FileContent::hash failed
- Execution of <DiskFileSystem as FileSystem>::read failed
- reading file "/private/tmp/nft-symlink-repro/data/dir-link"
- Is a directory (os error 21)

Expected: the build succeeds, as it does on 16.2.11 — a symlink to a directory inside a traced/included path should be traversed or skipped, not treated as a regular file.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.6.0
  Available memory (MB): 36864
  Available CPU cores: 12
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: 11.9.0
Relevant Packages:
  next: 16.3.0 // Latest available version is detected (16.3.0).
  eslint-config-next: N/A
  react: 19.2.0
  react-dom: 19.2.0
  typescript: 6.0.2
Next.js Config:
  output: N/A

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

Turbopack, Output (export/standalone)

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

next build (local), Vercel (Deployed)

Additional context

Root cause, from reading the v16.3.0 source:

  1. crates/next-api/src/nft.rs (get_glob_includes, around L198) classifies glob results with:

    let (DirectoryEntry::File(file_path) | DirectoryEntry::Symlink(file_path)) = entry
    else { continue; };
    result.push(file_path.clone());

    Every symlink is pushed into the includes list as if it were a file — the symlink target's type is never checked.

  2. crates/next-api/src/nft_json.rs (NftJsonAsset::content) — new in 16.3 — content-hashes every include for the new fileHashes field:

    file_path.read().hash(hash_salt, HashAlgorithm::Xxh3Hash128Hex)

    read() on a symlink-to-directory fails with EISDIR, which surfaces as a fatal TurbopackInternalError.

On 16.2 the misclassified entry was only listed in the .nft.json, so it was harmless; the unconditional hashing added in 16.3 turns it into a build-breaking panic. The same DirectoryEntry::File | DirectoryEntry::Symlink pattern is still present on current canary, so canary is affected too.

Notes:

  • outputFileTracingExcludes does not work around this — the panic happens while hashing the includes collection, before excludes are relevant.
  • In pnpm monorepos no unusual config is needed to hit this: any workspace package listed in serverExternalPackages whose node_modules contains a workspace link (pnpm creates node_modules/@scope/pkg → ../../sibling directory symlinks) crashes the same way. That's how we originally hit it in a production monorepo; the only workaround was removing the dependency so the symlink no longer exists.
  • Previously reported in Turbopack build panics (TurbopackInternalError EISDIR) when outputFileTracingIncludes glob matches a symlink to a directory #96255 with the same diagnosis, but that issue was closed by the triage bot because the reproduction was inlined rather than linked. This issue provides the public repro repository.

Metadata

Metadata

Assignees

No one assigned

    Labels

    OutputRelated to the the output configuration option.TurbopackRelated to Turbopack with Next.js.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions