The problem
Some npm packages use dynamic require() calls internally —
typically for worker threads, transports, plugins, or conditional
loading. Examples: pino, pino-pretty, packages that load
plugins by string name.
Turbopack can’t statically resolve these dynamic requires, so it
externalizes the package. At runtime in a compiled binary,
those dynamic calls hit names like require("pino-142500b1eb3f4baf")
that don’t exist in any node_modules, and you see:
Failed to load external module pino-142500b1eb3f4baf:
ResolveMessage: Cannot find package ...The fix
Add the package to transpilePackages in your next.config.ts:
const nextConfig: NextConfig = {
adapterPath: "next-bun-compile",
transpilePackages: ["pino", "pino-pretty"],
};This forces Turbopack to fully compile the package source rather than deferring its dynamic requires to runtime. The package code ends up bundled into your chunks — no externalization, no resolver gymnastics needed.
When you need it
You’ll hit this for packages that:
- Use
require()with computed/variable specifiers (require(pluginName)) - Spawn worker threads via
new Worker(__filename)or similar - Conditionally load implementations by inspecting env vars at runtime
Pure ESM imports and statically-analyzable require("string-literal")
calls don’t need this — they work fine externalized.
When NOT to use it
transpilePackages doesn’t work for native modules (anything
that ships .node files or relies on dlopen). Native deps must stay
externalized; that’s what the resolver hook is for. Examples:
sharp— externalize, don’t transpilebcrypt— externalize, don’t transpilebetter-sqlite3— externalize, don’t transpile
For native deps with dynamic resolution, the runtime resolver hook handles things automatically. See How it works.
Diagnosing whether a package needs it
Symptom: runtime error of the form Failed to load external module <pkg>-<16-hex-chars>.
To verify it’s a “dynamic require” issue and not a missing-canonical issue:
- Run with
NEXT_BUN_COMPILE_VERBOSE=1at build time. next-bun-compile prints every alias it discovered and whether it resolved. - If the failing alias resolves cleanly at build but fails at runtime, the dynamic-require path inside the package is hitting an un-statically-analyzable code path.
- Add the package to
transpilePackagesand rebuild.
If verbose output shows the alias didn’t resolve at build, see Troubleshooting: alias didn’t resolve.