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

[Turbopack Nextjs 16]: Pino - Cannot find module './transport-stream'

Open CHC383 opened this issue 1 month ago • 31 comments

Summary: Discussions in this issue have diverged a bit, and there are mainly two cases:

  1. Turbopack + non-Vercel deployment. Broken since v16.0.3, maybe due to Turbopack starts to trace worker_threads (fixed #84766).
    • Errors (include but not limited to):
      • Parsing ecmascript source code failed (License file) - comment1
      • Can't resolve 'why-is-node-running'/'tap'/etc. (require statement) - comment2, comment3
    • Workaround:
      • Switch back to Webpack. next build --webpack
      • Or add pino, thread-stream and related pino transport target packages to serverExternalPackages, you might also need to add thread-stream as a dependency explicitly.
  2. Turbopack + Vercel deployement. This is the original case of this issue. Turbopack never works in this setup, #84766 was fixed in v16.0.3, and now #86099 is waiting for a fix. The only workaround is to use Webpack next build --webpack with serverExternalPackages, see Notes below.

Notes: From comment9

Pino is way too dynamic for Turbopack to be able to figure out all required files automatically: pinojs/pino@7dd79a3/lib/transport.js#L94

Pino itself has a documentation that talks about bundling, and there were already problems since Webpack + Vercel deployment, see comment10.

Also for in−depth details: https://github.com/pinojs/thread-stream/issues/184


Link to the code that reproduces this issue

https://github.com/CHC383/nextjs-turbopack-pino-reproduction

To Reproduce

  1. Deploy the code to Vercel
  2. Visit /api/log
  3. Check the log of the Vercel deployment

Current vs. Expected behavior

Current behavior

With Turbopack in NextJs v16, Pino worker thread throws the following error:

Uncaught Exception: Error: Cannot find module './transport-stream'
Require stack:
- /var/task/node_modules/.pnpm/[email protected]/node_modules/pino/lib/worker.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1383:15)
    at defaultResolveImpl (node:internal/modules/cjs/loader:1025:19)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1030:22)
    at Module._load (node:internal/modules/cjs/loader:1192:37)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
    at Module.require (node:internal/modules/cjs/loader:1463:12)
    at require (node:internal/modules/helpers:147:16)
    at Object.<anonymous> (/var/task/node_modules/.pnpm/[email protected]/node_modules/pino/lib/worker.js:7:36)
    at Module._compile (node:internal/modules/cjs/loader:1706:14)

Expected behavior

Pino worker thread works without any exception, which is the behavior of using WebPack.

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #84-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep  5 22:36:38 UTC 2025
  Available memory (MB): 15839
  Available CPU cores: 20
Binaries:
  Node: 22.21.1
  npm: 11.6.2
  Yarn: N/A
  pnpm: 10.20.0
Relevant Packages:
  next: 16.0.3 // Latest available version is detected (16.0.3).
  eslint-config-next: N/A
  react: 19.2.0
  react-dom: 19.2.0
  typescript: 5.9.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)

Vercel (Deployed)

Additional context

@mischnic This is a follow up of #84766. It seems that with multiple targets, the pino/lib/worker.js is used and not traced by Turbopack properly. I have updated the reproduction code to trigger the new error.

CHC383 avatar Nov 13 '25 21:11 CHC383

We seem to be running into the same issue and it started happening with Next.js 16.0.3. The error looks a bit different though. Dev works fine, but the Turbopack build spits out a very long stacktrace with weird stuff like this:

./node_modules/thread-stream/LICENSE:1:5
Parsing ecmascript source code failed
> 1 | MIT License
    |     ^^^^^^^
  2 |
  3 | Copyright (c) 2021 Matteo Collina
  4 |

Expected ';', '}' or <eof>

Import trace:
  Instrumentation:
    ./node_modules/thread-stream/LICENSE
    ./node_modules/thread-stream/index.js
    ./node_modules/pino/lib/transport.js
    ./node_modules/pino/pino.js
    ./lib/logger.ts
    ./server/services/queue.service.ts
    ./instrumentation.ts

soulchild avatar Nov 14 '25 06:11 soulchild

By the way: Some official way to get structured (JSON) logging with Next.js would be awesome. 😉

soulchild avatar Nov 14 '25 06:11 soulchild

As a stop-gap solution, marking pacakges with serverExternalPackages seems to work

const nextConfig: NextConfig = {
  serverExternalPackages: ['pino', 'pino-pretty', 'thread-stream'],
};

abhijit945 avatar Nov 17 '25 15:11 abhijit945

I'm afraid that adding pino to serverExternalPackages is the only solution here. Pino is way too dynamic for Turbopack to be able to figure out all required files automatically: https://github.com/pinojs/pino/blob/7dd79a3370f34e0c092dcbc2c6fb1748afcc1cdd/lib/transport.js#L94

But we should probably just add it to the default externals list, so that people don't have to configure it.

mischnic avatar Nov 18 '25 14:11 mischnic

Pino is way too dynamic for Turbopack to be able to figure out all required files automatically. [...] But we should probably just add it to the default externals list, so that people don't have to configure it.

Good idea, but makes me wonder why Pino worked fine up until and including Next.js 16.0.2?

soulchild avatar Nov 18 '25 16:11 soulchild

If I install the pino from the nextjs app directly, the serverExternalPackages solution is good. But when I use the nextjs with turborepo and has this dependency list: apps/next-app ---> packages/pino-log ---> pino, the serverExternalPackages solution cannot resolve the issue.

lesliechueng1996 avatar Nov 18 '25 17:11 lesliechueng1996

@mischnic Having pino in the serverExternalPackages list might resolve the error @soulchild encountered, but it doesn't resolve the error I posted, because pino is already in the list (see reproduction code).

pino needs to be in the serverExternalPackages since using Webpack if the application is deployed in the Vercel environment, please see

  • https://github.com/pinojs/pino/blob/main/docs/bundling.md
  • https://github.com/pinojs/pino/issues/1964#issuecomment-2299477588

Good idea, but makes me wonder why Pino worked fine up until and including Next.js 16.0.2?

What is the setup you have with 16.0.2 (Turbopack/Webpack, Vercel/Self-hosted, standalone or not, etc.)? One of the changes of Turbopack since 16.0.3 is the fix of https://github.com/vercel/next.js/issues/84766.

CHC383 avatar Nov 18 '25 19:11 CHC383

What is the setup you have with 16.0.2

  • Turbopack
  • Self-hosted (Docker/Kubernetes)
  • Standalone

soulchild avatar Nov 18 '25 19:11 soulchild

I have the same issue, I can point that the issue occurs with v16.0.3 specifically:

  • v16.0.1 works
  • v16.0.3 crashes with the same error as above
  • v16.0.3 with serverExternalPackages works

matbour avatar Nov 19 '25 14:11 matbour

I tested with the serverExternalPackages stopgap but it breaks. On my end it's ignoring the exclusion of thread-stream

./node_modules/.pnpm/[email protected]/node_modules/thread-stream/test/helper.js:33:15
Module not found: Can't resolve 'why-is-node-running'
  31 |
  32 | if (process.env.SKIP_PROCESS_EXIT_CHECK !== 'true') {
> 33 |   const why = require('why-is-node-running')
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  34 |   setInterval(why, 10000).unref()
  35 | }
  36 |

Currently reverted to using webpack with this config:

webpack: (config) => {
    config.externals.push("pino-pretty", "lokijs", "encoding");
    return config;
  },

jmanywhere avatar Nov 19 '25 21:11 jmanywhere

I had the same problem on a turborepo monorepo, what solved it for me:

  • made sure pino and pino-pretty was in the next app's package.json since I'm using Bun's isolated linker
  • add serverExternalPackages: ['pino', 'pino-pretty', 'thread-stream'] to my next.config.ts

My question is: why it used to work on 16.0.1 and now it breaks? It doesn't make sense for a patch change to break something like this, does it?

iamleniac avatar Nov 20 '25 13:11 iamleniac

Stack:

  • self-hosted/docker
  • standalone
  • turbopack

It worked with Next 16.0.1. With Next 16.0.3 it crashes with the error shown.

For now, I’ve switched to Webpack while waiting for clarification.

giovanni-depalma avatar Nov 20 '25 14:11 giovanni-depalma

Having the same problems, and recreated a reproduction repo in my issue here https://github.com/vercel/next.js/issues/86458

I found out that it stopped working in version 16.0.2-canary.7

aulonm avatar Nov 26 '25 11:11 aulonm

Having the same issue in 16.0.6

I have set serverExternalPackages as described above. I am using Turbopack. We host our app on Vercel, but this is breaking locally with pnpm build

[1] Module not found: Can't resolve 'tap'
[1]   1 | 'use strict'
[1]   2 |
[1] > 3 | const t = require('tap')
[1]     |           ^^^^^^^^^^^^^^
[1]   4 |
[1]   5 | if (process.env.CI) {
[1]   6 |   t.skip('skip on CI')
[1]
[1]
[1]
[1] Import trace:
[1]   Client Component SSR:
[1]     ./node_modules/.pnpm/[email protected]/node_modules/thread-stream/test/string-limit.test.js [Client Component SSR]
[1]     ./node_modules/.pnpm/[email protected]/node_modules/thread-stream/index.js [Client Component SSR]
[1]     ./node_modules/.pnpm/[email protected]/node_modules/pino/lib/transport.js [Client Component SSR]
[1]     ./node_modules/.pnpm/[email protected]/node_modules/pino/pino.js [Client Component SSR]```

daviseford avatar Dec 01 '25 15:12 daviseford

The fixes above that add pino related packages to serverExternalPackages doesn't fix it for me.

./node_modules/.pnpm/[email protected]/node_modules/thread-stream/test/transpiled.test.js:3:18
Module not found: Can't resolve 'tap'
  1 | 'use strict'
  2 |
> 3 | const { test } = require('tap')
    |                  ^^^^^^^^^^^^^^
  4 | const { join } = require('path')
  5 | const { file } = require('./helper')
  6 | const ThreadStream = require('..')



Import traces:
  App Route:
    ./node_modules/.pnpm/[email protected]/node_modules/thread-stream/test/transpiled.test.js
    ./node_modules/.pnpm/[email protected]/node_modules/thread-stream/index.js
    ./node_modules/.pnpm/[email protected]/node_modules/pino/lib/transport.js
    ./node_modules/.pnpm/[email protected]/node_modules/pino/pino.js
    ./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/payload/dist/utilities/logger.js

columk1 avatar Dec 03 '25 21:12 columk1

Security Advisory: CVE-2025-66487 so i update 16.0.1 -> 16.0.7 but this issue popup

install pino and serverExternalPackages not work for me, and webpack build not a option...

----solve----- install pino -> install pino and thread-stream

wslp12 avatar Dec 04 '25 02:12 wslp12

facing same issue after upgrading to v16.0.7, though everything is fine on v16.0.1

./node_modules/.bun/[email protected]/node_modules/thread-stream/test/transpiled.test.js:3:18
Module not found: Can't resolve 'tap'
  1 | 'use strict'
  2 |
> 3 | const { test } = require('tap')
    |                  ^^^^^^^^^^^^^^
  4 | const { join } = require('path')
  5 | const { file } = require('./helper')
  6 | const ThreadStream = require('..')



Import trace:
  Client Component SSR:
    ./node_modules/.bun/[email protected]/node_modules/thread-stream/test/transpiled.test.js [Client Component SSR]
    ./node_modules/.bun/[email protected]/node_modules/thread-stream/index.js [Client Component SSR]
    ./node_modules/.bun/[email protected]/node_modules/pino/lib/transport.js [Client Component SSR]
    ./node_modules/.bun/[email protected]/node_modules/pino/pino.js [Client Component SSR]
    ./node_modules/.bun/@[email protected]/node_modules/@walletconnect/logger/dist/index.es.js [Client Component SSR]
    ./node_modules/.bun/@[email protected]+e8a3dd39f5b02c32/node_modules/@walletconnect/utils/dist/index.js [Client Component SSR]
    ./node_modules/.bun/@[email protected]+7b56cfec410038ca/node_modules/@walletconnect/ethereum-provider/dist/index.js [Client Component SSR]
    ./node_modules/.bun/@[email protected]+d827b7e707306ea4/node_modules/@privy-io/react-auth/dist/esm/EmbeddedWalletConnectingScreen-D50stmJi.mjs [Client Component SSR]
    ./node_modules/.bun/@[email protected]+d827b7e707306ea4/node_modules/@privy-io/react-auth/dist/esm/index.mjs [Client Component SSR]

deepso7 avatar Dec 04 '25 06:12 deepso7

I'm using webpack temporarily until this is fixed. next build --webpack

ThanosDi avatar Dec 04 '25 09:12 ThanosDi

It's a bloodbath in here

jonkwheeler avatar Dec 04 '25 14:12 jonkwheeler

Interesting, because adding the following to my next.config.js does indeed solve the problem for us, even with Next.js 16.0.7 and TurboPack:

serverExternalPackages: ['pino', 'pino-pretty', 'thread-stream']

soulchild avatar Dec 04 '25 15:12 soulchild

Interesting, because adding the following to my next.config.js does indeed solve the problem for us, even with Next.js 16.0.7 and TurboPack:

serverExternalPackages: ['pino', 'pino-pretty', 'thread-stream']

It did not for us. Specifically getting this error with [email protected].

jonkwheeler avatar Dec 04 '25 15:12 jonkwheeler

Security Advisory: CVE-2025-66487 so i update 16.0.1 -> 16.0.7 but this issue popup

install pino and serverExternalPackages not work for me, and webpack build not a option...

----solve----- install pino -> install pino and thread-stream

Say more!

jonkwheeler avatar Dec 04 '25 15:12 jonkwheeler

I had the same problem on a turborepo monorepo, what solved it for me:

  • made sure pino and pino-pretty was in the next app's package.json since I'm using Bun's isolated linker
  • add serverExternalPackages: ['pino', 'pino-pretty', 'thread-stream'] to my next.config.ts

My question is: why it used to work on 16.0.1 and now it breaks? It doesn't make sense for a patch change to break something like this, does it?

This works for me on 16.0.7 as it worked on 16.0.3, still, turbopack should not break like that and falling back to webpack should not be an option since turbopack is now the default production ready thing.

iamleniac avatar Dec 04 '25 15:12 iamleniac

In case it helps anyone, we fixed our build with a pino shim and thread-stream shim.

This is for using turbopack, after RSC mess, upgrading next.js to 16.0.7.

// src/shims/pino.ts

type PinoLevel =
  | 'fatal'
  | 'error'
  | 'warn'
  | 'info'
  | 'debug'
  | 'trace'
  | 'silent'

export interface PinoLogger {
  level: PinoLevel
  fatal: (...args: unknown[]) => void
  error: (...args: unknown[]) => void
  warn: (...args: unknown[]) => void
  info: (...args: unknown[]) => void
  debug: (...args: unknown[]) => void
  trace: (...args: unknown[]) => void
  child: (_bindings?: Record<string, unknown>) => PinoLogger
}

// This mimics the shape of pino.levels that @walletconnect/logger uses:
// - levels.values.<level> -> number
// - (optionally) levels.labels[number] -> level
export const levels = {
  labels: {
    10: 'trace',
    20: 'debug',
    30: 'info',
    40: 'warn',
    50: 'error',
    60: 'fatal',
  } as Record<number, PinoLevel>,
  values: {
    trace: 10,
    debug: 20,
    info: 30,
    warn: 40,
    error: 50,
    fatal: 60,
  } as Record<Exclude<PinoLevel, 'silent'>, number>,
} as const

export interface PinoOptions {
  level?: PinoLevel
  // add more if something complains at runtime
}

function createConsoleMethod(fallback: (...args: unknown[]) => void) {
  function logMethod(...args: unknown[]): void {
    fallback(...args)
  }

  return logMethod
}

export default function pino(
  _options?: PinoOptions,
  _destination?: unknown,
): PinoLogger {
  const level: PinoLevel = _options?.level ?? 'info'

  const logger: PinoLogger = {
    level,
    fatal: createConsoleMethod(console.error),
    error: createConsoleMethod(console.error),
    warn: createConsoleMethod(console.warn),
    info: createConsoleMethod(console.log),
    debug: createConsoleMethod(console.debug ?? console.log),
    trace: createConsoleMethod(console.debug ?? console.log),
    child(_bindings?: Record<string, unknown>): PinoLogger {
      return logger
    },
  }

  return logger
}
// src/shims/thread-stream.ts

type ThreadStreamOptions = {
  worker?: string
  [key: string]: unknown
}

export default class ThreadStream {
  // Minimal constructor signature; pino normally passes an options object.
  constructor(_opts: ThreadStreamOptions) {}

  write(_chunk: unknown): void {
    // no-op
  }

  end(): void {
    // no-op
  }

  on(_event: string, _listener: (...args: unknown[]) => void): void {
    // no-op
  }
}

Add this to your next.config.ts

  turbopack: {
    resolveAlias: {
      // All imports of the `pino` package go to our shim.
      pino: './src/shims/pino.ts',

      // Optional belt-and-suspenders: if *anything* tries to import thread-stream directly,
      // send it to a no-op stub instead of the real package.
      'thread-stream': './src/shims/thread-stream.ts',
    },
  },

This worked for us. Would love a different longer term solution to this.

jonkwheeler avatar Dec 04 '25 15:12 jonkwheeler

still not working for me even with @jonkwheeler 's fix. I'm using payload

krankos avatar Dec 04 '25 15:12 krankos

For me, using webpack wasn’t a solution. For now, I went with the following solution:

pnpm install pino [email protected]

and adding to next.config.js:

/**
* pino, pino-pretty and thread-stream are here to fix this issue:
* https://github.com/vercel/next.js/issues/86099#issuecomment-3610573089
*
* when this problem fixed, we can remove these packages from the serverExternalPackages and from package.json
*/
serverExternalPackages: ["pino", "pino-pretty", "thread-stream"],

gstcarv avatar Dec 04 '25 15:12 gstcarv

Alright, that was helpful @gstcarv. I got this to work for me...

and adding to next.config.js:

/**
* pino, pino-pretty and thread-stream are here to fix this issue:
* https://github.com/vercel/next.js/issues/86099#issuecomment-3610573089
*
* when this problem fixed, we can remove these packages from the serverExternalPackages and from package.json
*/
serverExternalPackages: ["pino", "pino-pretty", "thread-stream"],

Which output a verbose message.

> [email protected] next:build /Users/jon/agora_code/dashboard
> next build

   ▲ Next.js 16.0.7 (Turbopack)
   - Environments: .env.local
   - Experiments (use with caution):
     · optimizePackageImports

   Creating an optimized production build ...
Turbopack build encountered 2 warnings:
./node_modules/.pnpm/@[email protected]/node_modules/@walletconnect/logger/dist
Package pino can't be external
The request pino matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (10.1.0) compared to the package requested from the importing module (7.11.0).
Make sure to install the same version of the package in both locations.


./node_modules/.pnpm/[email protected]/node_modules/pino/lib
Package thread-stream can't be external
The request thread-stream matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (3.1.0) compared to the package requested from the importing module (0.15.2).
Make sure to install the same version of the package in both locations.

So I did this.

pnpm install [email protected] [email protected] --save-exact

and the build worked.

jonkwheeler avatar Dec 04 '25 16:12 jonkwheeler

hmm, but sadly I've 2 packages which are using pino and thread-stream with 2 different versions ;_;

Turbopack build encountered 2 warnings:
./node_modules/.bun/@[email protected]/node_modules/@walletconnect/logger/dist
Package pino can't be external
The request pino matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (7.11.0) compared to the package requested from the importing module (10.0.0).
Make sure to install the same version of the package in both locations.


./node_modules/.bun/[email protected]/node_modules/pino/lib
Package thread-stream can't be external
The request thread-stream matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (0.15.2) compared to the package requested from the importing module (3.1.0).
Make sure to install the same version of the package in both locations.

deepso7 avatar Dec 05 '25 03:12 deepso7

hmm, but sadly I've 2 packages which are using pino and thread-stream with 2 different versions ;_;

Turbopack build encountered 2 warnings:
./node_modules/.bun/@[email protected]/node_modules/@walletconnect/logger/dist
Package pino can't be external
The request pino matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (7.11.0) compared to the package requested from the importing module (10.0.0).
Make sure to install the same version of the package in both locations.


./node_modules/.bun/[email protected]/node_modules/pino/lib
Package thread-stream can't be external
The request thread-stream matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (0.15.2) compared to the package requested from the importing module (3.1.0).
Make sure to install the same version of the package in both locations.

Remove serverExternalPackages and switch to webpack. That will solve this message

daviseford avatar Dec 05 '25 03:12 daviseford

hmm, but sadly I've 2 packages which are using pino and thread-stream with 2 different versions ;_;

Turbopack build encountered 2 warnings:
./node_modules/.bun/@[email protected]/node_modules/@walletconnect/logger/dist
Package pino can't be external
The request pino matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (7.11.0) compared to the package requested from the importing module (10.0.0).
Make sure to install the same version of the package in both locations.


./node_modules/.bun/[email protected]/node_modules/pino/lib
Package thread-stream can't be external
The request thread-stream matches serverExternalPackages (or the default list).
The package resolves to a different version when requested from the project directory (0.15.2) compared to the package requested from the importing module (3.1.0).
Make sure to install the same version of the package in both locations.

Remove serverExternalPackages and switch to webpack. That will solve this message

Yes, this solved my issue, but I really wanted to use turbopack. Switching to webpack increased the build time from 2mins to 3mins roughly

deepso7 avatar Dec 05 '25 03:12 deepso7