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の機能を使い始める時期を、自分のプロジェクトの都合で選べます。