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. Corepack and Yarn are gone01
- 2. Native modules installed under Node 24 refuse to load02
- 3. fs.rmdir with recursive now throws03
- 4. fs.W_OK disappeared, without an error04
- 5. A localStorage warning in your tests05
- 6. Removed APIs and flags06
- What kept working07
- Will npm refuse to install on Node 26?08
- Should you upgrade?09
- 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 enableIn a Dockerfile:
FROM node:26-bookworm-slim
RUN npm install -g corepack && corepack enable2. 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 rebuildFor 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 runOn 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:
- Search the repo and Dockerfiles for
corepackandyarn. Each one needs the Corepack fix. - Make sure nothing reuses a
node_modulesfrom Node 24: local folders, CI caches, Docker stages (section 2). - Search your CI logs on Node 24 for
DEP0147(rmdir recursive) andDEP0176(fs.F_OKand friends). Anything that prints them today breaks or changes on 26. - Run your tests once on Node 26 and look for
ExperimentalWarningandDEP0205. - Then switch the version in
.nvmrc,enginesand 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