Skip to content

Commit 4954704

Browse files
committed
Turbopack: Add a check for node version before allowing workerThreads
1 parent 962e5b1 commit 4954704

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
@@ -336,7 +336,7 @@ export const experimentalSchema = {
336336
webpackMemoryOptimizations: z.boolean().optional(),
337337
turbopackMemoryLimit: z.number().optional(),
338338
turbopackPluginRuntimeStrategy: z
339-
.enum(['workerThreads', 'childProcesses'])
339+
.enum(['workerThreads', 'childProcesses', 'forceWorkerThreads'])
340340
.optional(),
341341
turbopackMinify: z.boolean().optional(),
342342
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
@@ -566,9 +566,32 @@ export interface ExperimentalConfig {
566566
turbopackMemoryLimit?: number
567567

568568
/**
569-
* Selects the runtime backend used by Turbopack for Node.js evaluation.
570-
*/
571-
turbopackPluginRuntimeStrategy?: 'workerThreads' | 'childProcesses'
569+
* Selects the backend used by Turbopack for Node.js evaluation, e.g. webpack
570+
* loaders, Babel, or PostCSS.
571+
*
572+
* This defaults to `'childProcesses'`, which creates a pool of child node.js
573+
* processes and communciates with them over sockets.
574+
*
575+
* `'workerThreads'` should use less memory and CPU. It may become the default
576+
* in a future version of Next.js.
577+
*
578+
* Node.js 24.13.1+ or 25.4.0+ is required for `'workerThreads'` due to memory
579+
* safety bugs in older versions. If you use this option with an older Node.js
580+
* version, the setting is ignored and a warning is emitted. Bun and Deno are
581+
* assumed safe, and are not checked for compatibility.
582+
*
583+
* - Fix for memory safety issue: <https://github.com/nodejs/node/pull/55877>
584+
* - Backported to 25.4.0: <https://github.com/nodejs/node/pull/61400>
585+
* - Backported to 24.13.1: <https://github.com/nodejs/node/pull/61661>
586+
*
587+
* `'forceWorkerThreads'` behaves like `'workerThreads'` but skips the
588+
* version-gated downgrade. You should not use this option unless you're
589+
* confident that the version check in Next.js is wrong.
590+
*/
591+
turbopackPluginRuntimeStrategy?:
592+
| 'workerThreads'
593+
| 'childProcesses'
594+
| 'forceWorkerThreads'
572595

573596
/**
574597
* 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 {
@@ -1443,6 +1444,52 @@ function assignDefaultsAndValidate(
14431444
result.experimental.useCache = result.cacheComponents
14441445
}
14451446

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

0 commit comments

Comments
 (0)