Node.js 26が10月28日にLTSへ|24から上げると動かなくなる主な変更を、両方の版で比べて確認
Node.js 26が10月28日に長期サポート版になり、24は保守だけの段階に入った
Node.jsの開発チームは2026年10月28日に26.13.0を公開し、Node.js 26はActive LTS(長期サポート版)になりました。長期サポート版としての名前は「Lithium」です。開発チームが公開しているリリース日程では、これまでActive LTSだったNode.js 24は10月20日にMaintenance LTSへ移り、ここからは重要な修正とセキュリティ修正だけが出る段階です。24のサポートは2028年4月30日まで、26は2029年4月30日まで続きます。
Node.jsではこれまで、偶数番号の版が半年ほど「Current」として新機能を受け取ったあと、長期サポート版に切り替わってきました。Node.jsの告知によると、27からは年に1回の公開になり、すべての版が長期サポート版になります。26は2026年5月5日に26.0.0として公開され、26.0.0のリリースノートには、古いAPIの削除やネイティブアドオンの互換番号の変更など、24から上げると動かなくなる変更が並んでいます。
この記事では、10月26日に当時の最新版だった24.22.1と26.12.1の公式バイナリを手元のMacで並べて動かし、24では動いて26では動かなくなる主な変更を確かめた結果をまとめます。26.13.0は、リリースノートによると process.release などの版の情報を長期サポート版に合わせて更新しただけで、26.12.1からほかの変更は入っていない版です。11月7日には、その時点の最新版の26.13.0と24.22.2でも同じサンプルを動かし、結果が変わらないことを確かめました。最後に、自分のプロジェクトを26へ上げる前に行う点検の手順を紹介します。
この記事の要点
24で動いていたコードが、主な5つの変更で26ではエラーになった。24の警告だけでは見つからないものがあるので、26で動かして確かめる
日程
10月28日に26がLTS
24は2028年4月30日まで修正が続くので、点検してから上げられる。
26でエラーになった主な変更
5つ
古いストリームの読み込み、writeHeader、V8を直接使う24向けのアドオン、corepack、型変換のフラグ。
24での事前の警告
試した3つは出なかった
古いストリームの読み込み、writeHeader、module.register()は、--pending-deprecationを付けても24.22.1で警告なしに動いた。
Node-APIで作られたアドオン
そのまま動いた
今回の検証では、26で再ビルドが必要だったのはV8の機能を直接使うアドオンだけだった。
2026年10月26日に公式のNode.js 24.22.1と26.12.1で確認し、11月7日に24.22.2と26.13.0でも同じ結果を確認(macOS 27、Apple Silicon)。
24は2028年4月まで使えるので、26への切り替えは点検してからでよい
先に日程を並べます。24は10月20日に保守の段階に入りましたが、セキュリティ修正は2028年4月30日まで続き、26が長期サポート版になったあとも本番で使い続けられます。一方で、新しく作るプロジェクトやCIの標準の版は、26を前提に考える時期に入りました。
2026年5月5日
Node.js 26.0.0を公開。新機能を受け取るCurrentの段階。
2026年10月20日
Node.js 24がMaintenance LTSへ。重要な修正とセキュリティ修正だけになった。
2026年10月28日
Node.js 26がActive LTSへ。26.13.0で長期サポート版になり、推奨される版になった。
2028年4月30日
Node.js 24のサポート終了。これ以降はセキュリティ修正も出ない。
2029年4月30日
Node.js 26のサポート終了。
出典: Node.jsのリリース日程(nodejs/Release の schedule.json、2026年11月7日時点)と26.13.0のリリースノート。
24から26へ上げると、主な5つの変更でエラーになった
25.0.0と26.0.0のリリースノートに載っている削除と互換性の変更から、アプリや依存パッケージで使われていそうな5つを選び、同じコードを24.22.1と26.12.1で動かしました。24から26へ上げると、間の25.0.0での変更も同時に反映されます。5つのうちcorepackは25.0.0からの変更です。
1. require('_stream_readable')
24: 読み込めた
26: MODULE_NOT_FOUND
直し方: node:stream から読み込む
2. res.writeHeader()
24: 200で応答できた
26: TypeError
直し方: res.writeHead() に変える
3. 24向けにビルドしたアドオン
24: 読み込めた
26: V8を直接使う版はERR_DLOPEN_FAILED
直し方: 26で再ビルドする
4. corepack enable
24: 同梱されている
26: command not found
直し方: npmでcorepackを入れる
5. --experimental-transform-types
24: 警告付きでenumが動いた
26: bad option
直し方: TypeScriptで変換してから動かす
古いストリームのモジュールとwriteHeaderは、26で削除された
1つ目は、Node.jsの内部モジュールだった _stream_readable や _stream_transform などを、名前で直接 require する書き方です。2つ目は、HTTPサーバーの応答で res.writeHead() の古い別名 res.writeHeader() を呼ぶ書き方です。どちらも26.0.0で完全に削除されました。
次のサンプルを両方の版で動かしました。ストリームは公開されている node:stream から読み込めば、両方の版で同じように使えます。
// legacy-stream.cjs
try { const R = require('_stream_readable'); console.log('_stream_readable: loaded', typeof R); }
catch (e) { console.log('_stream_readable:', e.code, e.message.split('\n')[0]); }
const { Readable } = require('node:stream'); console.log('node:stream Readable:', typeof Readable);
// write-header.cjs(ローカルにHTTPサーバーを立て、fetchで1回だけ呼ぶ)
try { res.writeHeader(200, { 'Content-Type': 'text/plain' }); res.end('writeHeader ok'); }
catch (e) { res.writeHead(500); res.end('ERR ' + e.constructor.name + ': ' + e.message); } // 例外を500で返す
===== node v24.22.1
_stream_readable: loaded function
node:stream Readable: function
status 200 | writeHeader ok
===== node v26.12.1
_stream_readable: MODULE_NOT_FOUND Cannot find module '_stream_readable'
node:stream Readable: function
status 500 | ERR TypeError: res.writeHeader is not a function
自分のコードで使っていなくても、古い依存パッケージの中で使われていることがあります。26でサーバーを起動した直後に Cannot find module '_stream_transform' のようなエラーが出たら、まずこの削除を疑います。
V8を直接使うネイティブアドオンは、26で再ビルドが必要
3つ目はネイティブアドオンです。画像処理やデータベース接続のnpmパッケージには、C/C++で書かれた部品(.node ファイル)を含むものがあります。26ではこの部品の互換番号(NODE_MODULE_VERSION)が24の137から147に上がりました。
ただし、影響を受けるのはアドオンの作り方によります。V8やNode.jsの内部APIを直接使う作り方と、Node.jsの版をまたいで使えるように用意された「Node-API」を使う作り方の2種類で、最小のアドオンを1つずつ作り、24向けにビルドしてから26で読み込みました。
== node 24.22.1 (built with 24.22.1)
v8addon: v8 addon ok
napiaddon: node-api addon ok
== node 26.12.1 (built with 24.22.1)
v8addon: ERROR ERR_DLOPEN_FAILED The module '.../build/Release/v8addon.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
the module (for instance, using `npm rebuild` or `npm install`).
napiaddon: node-api addon ok
== rebuild with 26.12.1
v8addon: v8 addon ok
napiaddon: node-api addon ok
Node-APIで作られたアドオンは、24向けにビルドしたものが26でもそのまま動きました。V8を直接使うアドオンは26で読み込めず、26でビルドし直すと動きました。V8を直接使うアドオン(NANというライブラリで書かれたものなど)を含むプロジェクトでは、24でインストールした node_modules をそのまま26で使うと、このエラーで起動に失敗します。Dockerのキャッシュや、CIで node_modules を使い回している場合も同じです。
corepackは同梱されなくなり、型変換のフラグは削除された
4つ目はcorepackです。corepackは、pnpmやYarnをプロジェクトで決めた版で使うためにNode.jsへ同梱されていたツールで、corepack enable を実行してから pnpm install する手順で使われてきました。Node.jsのドキュメントには、25以降はcorepackを同梱せず、npmで配布されているcorepackを使うよう書かれています。26.12.1の bin フォルダにも、node、npm、npx だけが入っていました。
$ ls node-v24.22.1-darwin-arm64/bin
corepack node npm npx
$ ls node-v26.12.1-darwin-arm64/bin
node npm npx
$ corepack enable # Node.js 26.12.1 だけを PATH に入れた状態
zsh:1: command not found: corepack
5つ目は、TypeScriptの enum のように、型を消すだけでは動かない書き方を変換して実行する --experimental-transform-types フラグです。24では実験的機能の警告付きで enum を含むファイルが動きましたが、26ではフラグ自体が bad option として拒否されました。型注釈を消すだけで動く .ts ファイルは、どちらの版でもフラグなしで動いています。
$ node --experimental-transform-types enum.ts
# 24.22.1
enum: 1
(node:30068) ExperimentalWarning: Transform Types is an experimental feature and might change at any time
# 26.12.1
node: bad option: --experimental-transform-types
$ node app.ts # 型注釈だけのファイル(24.22.1・26.12.1とも)
type stripping ok: Ken 30
このほか、ESMの読み込みを差し替える module.register() は、26で実行すると DEP0205 の警告が出るようになりました。こちらは今のところ動作は続き、警告の文面は後継の module.registerHooks() を使うよう案内しています。
25.0.0で削除されたfs.F_OKやSlowBufferも、26へ上げると一緒に使えなくなる
25.0.0では、ファイルの存在確認に使う fs.F_OK などの定数、SlowBuffer、fs.rmdir() の recursive オプションも削除されています。24から26へ直接上げると、これらも同時に使えなくなります。
===== node 24.22.1
fs.F_OK: 0 | fs.constants.F_OK: 0
SlowBuffer: function
rmdirSync recursive: removed true
[DEP0176] DeprecationWarning: fs.F_OK is deprecated, use fs.constants.F_OK instead
[DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true }) instead
===== node 26.12.1
fs.F_OK: undefined | fs.constants.F_OK: 0
SlowBuffer: undefined
rmdirSync recursive: ERR_INVALID_ARG_VALUE
rmSync recursive: removed true
このうち fs.F_OK、fs.R_OK、fs.W_OK、fs.X_OK の定数は、26ではエラーにならず undefined になるだけです。そのため、権限を確かめるコードが黙って存在の確認だけに変わります。読み取り専用のファイルに書き込めるかを fs.accessSync(file, fs.W_OK) で確かめると、24では EACCES になり、26では例外なしで通りました。
// access-wok.cjs(権限444のファイルに対して実行)
===== node 24.22.1
accessSync(readonly, fs.W_OK): EACCES
accessSync(readonly, fs.constants.W_OK): EACCES
===== node 26.12.1
accessSync(readonly, fs.W_OK): passed (no error)
accessSync(readonly, fs.constants.W_OK): EACCES
new SlowBuffer: TypeError require(...).SlowBuffer is not a constructor
この変更は、26でテストを流しても失敗として現れないことがあります。24では DEP0176 などの DeprecationWarning が出ていたので、24のログに出ている警告と、下の点検手順の検索で探します。fs.constants.F_OK などの定数、Buffer.allocUnsafeSlow()、fs.rmSync(path, { recursive: true }) に書き換えれば、両方の版で動きます。
26では日付のTemporalなど、新しいJavaScriptの機能が使える
26へ上げると使えるようになる機能もあります。26ではJavaScriptエンジンのV8が14.6に上がり、日付と時刻を扱う新しいAPIのTemporalが最初から使えるようになりました。同じサンプルで確かめた結果です。
===== node v24.22.1
Temporal: undefined
Map.getOrInsert: not available
Iterator.concat: not available
V8 13.6.233.17-node.58 | undici 7.30.1 | NODE_MODULE_VERSION 137
===== node v26.12.1
Temporal: object
PlainDate add: 2028-04-28
Map.getOrInsert: 1
Iterator.concat: 1,2,3
V8 14.6.202.34-node.40 | undici 8.12.2 | NODE_MODULE_VERSION 147
26.12.1の「PlainDate add」の行は、2026年10月28日の18か月後をTemporalで計算した結果です。Temporalは、月末や時差を含む日付計算を Date より正確に書けるAPIです。Map の getOrInsert() は「キーがなければ値を入れて返す」処理を1行で書けるメソッドです。どちらも24では使えないので、使い始めるとそのコードは24では動かなくなります。
26へ上げる前に、依存の中の書き方を点検して26で動かす
24.22.1で --pending-deprecation を付けて動かしても、古いストリームの読み込み、writeHeader()、module.register() の3つは何の警告も出さずに動きました。fs.F_OK のように24で警告が出るものもありますが、24の警告ログだけでは、26でエラーになる変更をすべては見つけられません。上げる前に、次の順で点検します。
1. 依存パッケージの中を検索する
プロジェクトのフォルダで下のgrepを実行し、古いストリームのモジュールを名前で読み込むファイルとwriteHeaderを呼ぶファイルを探す。自分のコードでは、fs.F_OKなどの定数とSlowBufferも検索する。見つかったパッケージは更新するか置き換える。
2. node_modulesを26で入れ直す
24で作ったnode_modulesやDockerのキャッシュを使い回さず、package-lock.jsonのあるプロジェクトなら26でnpm ciを実行して入れ直す。ネイティブアドオンは26向けのビルド済みバイナリを取得するか、その場でビルドされる。失敗したら、26に対応した版へ更新する。
3. corepackの手順を置き換える
CIやDockerfileにcorepack enableがあれば、先にnpm install -g corepackを実行するか、pnpmやYarnを直接インストールする手順に変える。
4. CIに26を足してテストを流す
24と26の両方でテストとサーバーの起動を確かめる。Linuxで公式バイナリを入れているCIやDockerイメージでは、まず26の node が起動するかを見る。26.13.0のリリースノートによると、25以降のLinux向け公式バイナリは libatomic というライブラリを使うので、ディストリビューションによっては別に入れる必要がある。24のログに出ているDeprecationWarningも合わせて直し、26で通ったら標準の版を切り替える。
$ grep -rlE "require\(\s*['\"](node:)?_stream_|from\s+['\"]_stream_|\.writeHeader\(" node_modules
node_modules/old-pkg/server.js
node_modules/old-pkg/index.js
$ grep -rnE "\bfs\.(F|R|W|X)_OK\b|SlowBuffer" src
src/app.js:2:fs.accessSync(p, fs.W_OK);
点検用に3つの模擬パッケージを用意して実行した結果。_stream_transform を名前で読み込み writeHeader を呼ぶ old-pkg だけが見つかった。node:stream を使うパッケージと、自分のフォルダ内の ./lib/_stream_readable.js を相対パスで読むパッケージは対象外になり、どちらも26で読み込めた。old-pkg は26で Cannot find module '_stream_transform' のエラーになった。
grepは、コメントの中の文字列や、HTTPと関係のない同じ名前のメソッドも拾います。逆に、改行をはさんだ require や動的な import() は拾えません。見つかったファイルは、実際に require や呼び出しに使っているかを開いて確かめてから、パッケージの更新を判断します。
24のサポートは2028年4月まで続くので、26が長期サポート版になった今も、本番のサーバーを急いで切り替える必要はありません。CIに26を足して上の4つの手順を済ませておけば、Temporalのような26の機能を使い始める時期を、自分のプロジェクトの都合で選べます。












