Skip to content
Nilay Kabariya

Entry No.057·Tested·Updated ·8 min read

Node 26 vs Node 24: what breaks when you upgrade, tested

Node 26 is LTS since October 28 (26.13.0). I moved real projects from Node 24 on Windows, Linux and Docker: what broke, the exact errors, and the fixes.

Verdict

Most projects move over cleanly: jsonwebtoken, bcrypt, sharp, better-sqlite3 13, tsx and Vitest all worked unchanged. What broke: Corepack and Yarn are gone (Docker builds fail with exit code 127), native modules installed under Node 24 refuse to load, fs.rmdir with recursive now throws (prisma generate with zod-prisma-types fails on the second run), and fs.W_OK disappeared without an error.

Tested on

Node
26.13.0 LTS and 26.11.1 / 24.21.0
npm
11.20.0 / 11.19.0 (bundled)
OS
Windows 11 Pro, Ubuntu (GitHub Actions), Docker node:26-bookworm-slim
Contents
  1. 1. Corepack and Yarn are gone01
  2. 2. Native modules installed under Node 24 refuse to load02
  3. 3. fs.rmdir with recursive now throws03
  4. 4. fs.W_OK disappeared, without an error04
  5. 5. A localStorage warning in your tests05
  6. 6. Removed APIs and flags06
  7. What kept working07
  8. Will npm refuse to install on Node 26?08
  9. Should you upgrade?09
  10. How this was tested10

Node 26 became the LTS release on October 28, 2026, as 26.13.0 “Lithium”, and Node 24 moved to maintenance on October 20 (both dates from the official release schedule). This is the point where teams on 24 start upgrading. Between them sit two major versions (25 and 26), so the list of changes is long. I wanted the short list: the ones that actually break a project.

I took the latest of each, Node 24.21.0 and 26.11.1, installed real packages under Node 24, then ran the same code on Node 26 on Windows 11, on Ubuntu in GitHub Actions, and in the official Docker images.

Change What you see on Node 26 Loud or silent?
Corepack and Yarn removed 'corepack' is not recognized / yarn: not found Loud
Native modules built on 24 NODE_MODULE_VERSION 137 … requires NODE_MODULE_VERSION 147 Loud
fs.rmdir(…, { recursive: true }) The property 'options.recursive' is no longer supported Loud
fs.F_OK, R_OK, W_OK, X_OK removed Nothing. Permission checks quietly pass Silent
Web Storage on by default localStorage is not available because --localstorage-file was not provided Warning
_stream_* modules, SlowBuffer, writeHeader, --experimental-transform-types removed Loud
module.register() [DEP0205] DeprecationWarning Warning

1. Corepack and Yarn are gone

Node 25 stopped shipping Corepack, so Node 26 doesn’t have it either. On Windows, with the official zip (what nvm-windows and fnm install):

C:\project> corepack enable
'corepack' is not recognized as an internal or external command,
operable program or batch file.

The Node 24.21.0 zip still has corepack.cmd next to node.exe. The 26.11.1 zip doesn’t.

Docker is worse, because the official node:24 images also had Yarn 1.22.22 preinstalled, and the node:26 images have neither:

$ docker run --rm node:26-bookworm-slim sh -c 'yarn --version; corepack --version'
sh: 1: yarn: not found
sh: 1: corepack: not found

So a Dockerfile that worked for years stops building the day you change the tag:

#5 0.093 /bin/sh: 1: corepack: not found
ERROR: failed to build: failed to solve: process "/bin/sh -c corepack enable && yarn --version" did not complete successfully: exit code: 127

Corepack still exists, it just isn’t bundled. Install it from npm, then enable it as before:

npm install -g corepack
corepack enable

In a Dockerfile:

FROM node:26-bookworm-slim
RUN npm install -g corepack && corepack enable

2. Native modules installed under Node 24 refuse to load

Every Node major changes the native module ABI. Node 24 is NODE_MODULE_VERSION 137, Node 26 is 147. If you switch Node versions without reinstalling, any module that was compiled for the old version fails on first use. With better-sqlite3 12 installed under Node 24 and run on Node 26:

Error: The module '\\?\C:\project\node_modules\better-sqlite3\build\Release\better_sqlite3.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 137. This version of Node.js requires
NODE_MODULE_VERSION 147. Please try re-compiling or re-installing

This hits you when node_modules outlives the Node switch: nvm use 26 in an existing project, a cached node_modules in CI, or a Docker layer copied from a Node 24 stage.

Rebuild the native modules under Node 26:

npm rebuild

For CI, include the Node version in the node_modules cache key so a new major never reuses old binaries.

Not every native module has this problem. bcrypt 6, sharp and better-sqlite3 13 installed under Node 24 all ran on Node 26 unchanged, because they use Node-API or ship a binary for each Node version inside the package. better-sqlite3 13 is the easy fix for that one package (more on its binaries).

3. fs.rmdir with recursive now throws

fs.rmdir(path, { recursive: true }) was deprecated for years. Node 25 removed the option, and it throws instead of deleting:

TypeError [ERR_INVALID_ARG_VALUE]: The property 'options.recursive' is no longer supported. Received true

You rarely call it yourself. Dependencies do. The one I reproduced is zod-prisma-types 3.3.11 (the latest), with Prisma 6.19.3. The first prisma generate works. The second, which has to clear the old output, fails:

✔ Generated Prisma Client (v6.19.3) to .\node_modules\@prisma\client in 78ms
Error while deleting old data in path C:\project\prisma\generated\zod: The property 'options.recursive' is no longer supported. Received true

On Node 24.21.0 the same command ran twice without a problem. The bug is open upstream. The same removal also broke dbgate, and a Windows-only fallback in Astro (since fixed).

In your own code, use fs.rm:

fs.rmSync(dir, { recursive: true, force: true });

For zod-prisma-types, delete the output folder before generating, so the package never needs to:

{
  "scripts": {
    "generate": "node -e \"require('fs').rmSync('prisma/generated/zod',{recursive:true,force:true})\" && prisma generate"
  }
}

4. fs.W_OK disappeared, without an error

Node 25 removed the old shortcuts fs.F_OK, fs.R_OK, fs.W_OK and fs.X_OK (the copies in fs.constants are still there). This one doesn’t throw. The values are just undefined, and fs.access with an undefined mode only checks that the file exists.

I made a file read-only and asked whether it was writable:

Node fs.W_OK fs.accessSync(file, fs.W_OK)
24.21.0 2 throws EPERM: not writable ✅
26.11.1 undefined no error: looks writable ❌

Any code that checks permissions this way now gets “yes” for every file that exists. Node 24 printed [DEP0176] DeprecationWarning: fs.F_OK is deprecated, so search your CI logs for DEP0176 before upgrading.

Use fs.constants:

fs.accessSync(file, fs.constants.W_OK);

5. A localStorage warning in your tests

Node 25 turned on Web Storage by default, and Node 26 leaves localStorage undefined unless you give it a file to store into. Touching it is enough for a warning, even typeof localStorage:

(node:9440) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.

In Vitest 5.0.3, a test in the node environment that only checks typeof localStorage printed this warning on Node 26 and nothing on Node 24. The test still passed. Tests in the jsdom environment passed with no warning, because jsdom supplies its own localStorage.

If you don’t use Node’s built-in localStorage, switch it off:

NODE_OPTIONS=--no-experimental-webstorage npx vitest run

On Windows PowerShell, set $env:NODE_OPTIONS="--no-experimental-webstorage" first. If you do want it, give it a file: node --localstorage-file=./storage.json app.js.

6. Removed APIs and flags

These all failed or vanished on Node 26.11.1 and worked on 24.21.0:

Removed On Node 26 Use instead
require('_stream_readable') (and _stream_writable, _stream_duplex, _stream_transform, _stream_passthrough, _stream_wrap) MODULE_NOT_FOUND require('node:stream').Readable etc.
--experimental-transform-types node.exe: bad option: --experimental-transform-types drop the flag for plain type stripping, or run with tsx
require('buffer').SlowBuffer undefined Buffer.allocUnsafeSlow()
response.writeHeader() undefined response.writeHead()
module.register() still works, prints [DEP0205] DeprecationWarning module.registerHooks()

jsonwebtoken is worth a mention: its dependency buffer-equal-constant-time 1.0.1 touches SlowBuffer only in an optional install() function that jsonwebtoken never calls, so signing and verifying worked on Node 26. Plain node app.ts (type stripping) and tsx 4.23.15 also worked unchanged.

What kept working

Package Node 26.11.1
jsonwebtoken 9.0.2 ✅ signed and verified a token
bcrypt 6.0.0 ✅ hashed (installed under Node 24, not rebuilt)
sharp 0.35.5 ✅ loaded (installed under Node 24)
better-sqlite3 13.0.3 ✅ ran a query (installed under Node 24)
tsx 4.23.15 ✅ ran a .ts file, no deprecation warning
Vitest 5.0.3 + jsdom 30.1.2 ✅ localStorage test passed
Prisma 6.19.3 client generation ✅ (zod-prisma-types aside)

New in Node 26, and available without a flag: Temporal (it was undefined on 24.21.0), V8 14.6, and undici 8 behind fetch. Node 26.11.1 still bundles npm 11, so npm 12’s changes only arrive if you upgrade npm yourself.

Will npm refuse to install on Node 26?

I checked the engines field of 21 popular packages, including Next.js 16, Vite 8, ESLint 10, Jest 30, Prisma, Puppeteer, Playwright, Wrangler and the Angular and NestJS CLIs. None of them excludes Node 26, so npm warn EBADENGINE is unlikely to come from the upgrade itself.

The opposite case is more likely. Nuxt 4.6.1 and Angular CLI 22 require ^24.15.0 on the Node 24 line, so if you stay on an older 24 you get the warning:

npm warn EBADENGINE Unsupported engine {
npm warn EBADENGINE   package: 'nuxt@4.6.1',
npm warn EBADENGINE   required: { node: '^22.22.3 || ^24.15.0 || >=26.0.0' },
npm warn EBADENGINE   current: { node: 'v24.14.0', npm: '11.9.0' }

Should you upgrade?

Yes, it’s been LTS since October 28, but check these five things first, in this order:

  1. Search the repo and Dockerfiles for corepack and yarn. Each one needs the Corepack fix.
  2. Make sure nothing reuses a node_modules from Node 24: local folders, CI caches, Docker stages (section 2).
  3. Search your CI logs on Node 24 for DEP0147 (rmdir recursive) and DEP0176 (fs.F_OK and friends). Anything that prints them today breaks or changes on 26.
  4. Run your tests once on Node 26 and look for ExperimentalWarning and DEP0205.
  5. Then switch the version in .nvmrc, engines and the Docker tag together.

One thing that is not a reason to wait: a memory regression reported this week (nodejs/node#66564, idle memory released late after a burst of load) affects Node 24.21.0 and 26.10.0 alike, so staying on 24 doesn’t avoid it.

How this was tested

Node 24.21.0 and 26.11.1 from the official Windows zips (checksums verified against SHASUMS256.txt), on Windows 11 Pro. Packages were installed under Node 24, then the same node_modules was run on both versions, so the native-module results show what happens when you switch versions in an existing project. The same checks ran on Ubuntu in GitHub Actions with setup-node, and the Docker results come from the official node:24-bookworm-slim and node:26-bookworm-slim images on a GitHub Actions runner. On October 29, the Linux checks and the Docker tags ran again with 26.13.0, the LTS release. The Linux checks and both Dockerfiles are public in nk-repro. Every message above is the real output.

— N.K., end of entry No.057

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.037

    npm 12 breaking changes, tested: what actually breaks

    The headline change, install scripts blocked by default, broke puppeteer, cypress, sqlite3 and Claude Code's npm package, and silently skipped lefthook's git hooks. esbuild, bcrypt, sharp and better-sqlite3 kept working. Every install reported success, and the only sign was a warning after the summary.

    TESTED6 minWindows
  2. No.055

    18 framework starters tested on Windows: what breaks

    15 of 18 starters work on Windows 11: 14 with no issues at all, 1 with a workaround. Every Remix 3 script still fails out of the box and works on Linux; Docusaurus's Windows build failure is fixed by SWC 1.16.21, Nuxt's Windows-only 500 is fixed in 4.6.1, and React Router's installer in 8.5.1. Three more break in ways that tutorials and CI scripts hit: Angular needs a newer Node 24, Electron Forge renamed its template, and Cloudflare's C3 can exit successfully with a broken project.

    TESTED8 minWindows
  3. No.093

    Fix: bigint Failed to load bindings, pure JS will be used

    bigint: Failed to load bindings, pure JS will be used. Why npm 12 shows it on every install, whether it matters, and how to get the native build back.

    > bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)

    FIXED3 minWindows

Post card · Every Sunday

What broke this week.

One email on Sundays: the Node, TypeScript, Python and AI SDK releases that broke builds that week, each one reproduced, with a link to the fix that worked. Never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text