Skip to content

Commit b5d0ef1

Browse files
committed
Turbopack: Add a check for node version before allowing workerThreads
1 parent b8b72ae commit b5d0ef1

3 files changed

Lines changed: 74 additions & 4 deletions

File tree

packages/next/src/server/config-schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ export const experimentalSchema = {
351351
webpackMemoryOptimizations: z.boolean().optional(),
352352
turbopackMemoryLimit: z.number().optional(),
353353
turbopackPluginRuntimeStrategy: z
354-
.enum(['workerThreads', 'childProcesses'])
354+
.enum(['workerThreads', 'childProcesses', 'forceWorkerThreads'])
355355
.optional(),
356356
turbopackMinify: z.boolean().optional(),
357357
turbopackFileSystemCacheForDev: z.boolean().optional(),

packages/next/src/server/config-shared.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,32 @@ export interface ExperimentalConfig {
628628
turbopackMemoryLimit?: number
629629

630630
/**
631-
* Selects the runtime backend used by Turbopack for Node.js evaluation.
632-
*/
633-
turbopackPluginRuntimeStrategy?: 'workerThreads' | 'childProcesses'
631+
* Selects the backend used by Turbopack for Node.js evaluation, e.g. webpack
632+
* loaders, Babel, or PostCSS.
633+
*
634+
* This defaults to `'childProcesses'`, which creates a pool of child node.js
635+
* processes and communciates with them over sockets.
636+
*
637+
* `'workerThreads'` should use less memory and CPU. It may become the default
638+
* in a future version of Next.js.
639+
*
640+
* Node.js 24.13.1+ or 25.4.0+ is required for `'workerThreads'` due to memory
641+
* safety bugs in older versions. If you use this option with an older Node.js
642+
* version, the setting is ignored and a warning is emitted. Bun and Deno are
643+
* assumed safe, and are not checked for compatibility.
644+
*
645+
* - Fix for memory safety issue: <https://github.com/nodejs/node/pull/55877>
646+
* - Backported to 25.4.0: <https://github.com/nodejs/node/pull/61400>
647+
* - Backported to 24.13.1: <https://github.com/nodejs/node/pull/61661>
648+
*
649+
* `'forceWorkerThreads'` behaves like `'workerThreads'` but skips the
650+
* version-gated downgrade. You should not use this option unless you're
651+
* confident that the version check in Next.js is wrong.
652+
*/
653+
turbopackPluginRuntimeStrategy?:
654+
| 'workerThreads'
655+
| 'childProcesses'
656+
| 'forceWorkerThreads'
634657

635658
/**
636659
* Enable minification. Defaults to true in build mode and false in dev mode.

packages/next/src/server/config.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { existsSync } from 'fs'
22
import { basename, extname, join, relative, isAbsolute, resolve } from 'path'
33
import { pathToFileURL } from 'url'
44
import findUp from 'next/dist/compiled/find-up'
5+
import semver from 'next/dist/compiled/semver'
56
import * as Log from '../build/output/log'
67
import * as ciEnvironment from '../server/ci-info'
78
import {
@@ -1453,6 +1454,52 @@ function assignDefaultsAndValidate(
14531454
result.experimental.useCache = result.cacheComponents
14541455
}
14551456

1457+
// Node.js version gate for turbopackPluginRuntimeStrategy: 'workerThreads'.
1458+
// Older Node.js versions have memory safety bugs in worker threads. Bun and
1459+
// Deno are not affected by this check.
1460+
{
1461+
const strategy = result.experimental.turbopackPluginRuntimeStrategy
1462+
const isForced = strategy === 'forceWorkerThreads'
1463+
if (strategy === 'workerThreads' || isForced) {
1464+
// Normalize 'forceWorkerThreads' → 'workerThreads' for Rust/serde
1465+
result.experimental.turbopackPluginRuntimeStrategy = 'workerThreads'
1466+
1467+
const isBun = !!process.versions.bun
1468+
const isDeno = !!process.versions.deno
1469+
if (!isBun && !isDeno) {
1470+
const nodeVersion = process.versions.node
1471+
const WORKER_THREADS_SAFE_RANGE = '>=24.13.1 <25.0.0 || >=25.4.0'
1472+
if (
1473+
!semver.satisfies(nodeVersion, WORKER_THREADS_SAFE_RANGE, {
1474+
includePrerelease: true,
1475+
})
1476+
) {
1477+
if (isForced) {
1478+
Log.warn(
1479+
`\`experimental.turbopackPluginRuntimeStrategy = ` +
1480+
`'forceWorkerThreads'\` has been enabled, but you're using ` +
1481+
`Node.js ${nodeVersion}, which has known memory safety bugs ` +
1482+
`with worker threads used from the Node-API. You may ` +
1483+
`experience crashes, segmentation faults, or other ` +
1484+
`instability. Upgrade to Node.js ${WORKER_THREADS_SAFE_RANGE}.`
1485+
)
1486+
} else {
1487+
Log.warn(
1488+
`\`experimental.turbopackPluginRuntimeStrategy = ` +
1489+
`'workerThreads'\` is set but has been ` +
1490+
`ignored because you're using Node.js ${nodeVersion}, which ` +
1491+
`has memory safety bugs in worker threads. Falling back to ` +
1492+
`'childProcesses'. Upgrade to Node.js ` +
1493+
`${WORKER_THREADS_SAFE_RANGE}.`
1494+
)
1495+
result.experimental.turbopackPluginRuntimeStrategy =
1496+
'childProcesses'
1497+
}
1498+
}
1499+
}
1500+
}
1501+
}
1502+
14561503
// Store the distDirRoot in the config before it is modified for development mode
14571504
;(result as NextConfigComplete).distDirRoot = result.distDir
14581505
// Pre-compute the effective hash salt (used by both Webpack and Turbopack).

0 commit comments

Comments
 (0)