From 4d7996fb5629e79d8183e4db015cc2b35d65cfdd Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:34:23 -0700 Subject: [PATCH 1/5] fs: ignore deleted dirs in recursive watch scan A recursively watched directory can be removed after a parent watcher observes it but before the non-native recursive watcher scans it. Ignore ENOENT from the directory scan so this deletion race does not emit an unhandled watcher error. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 PR-URL: https://github.com/nodejs/node/pull/63686 Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-06-01.md#jstest-failure Reviewed-By: Moshe Atlow Reviewed-By: Luigi Pinca --- lib/internal/fs/recursive_watch.js | 4 +- .../test-fs-watch-recursive-delete-race.js | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-fs-watch-recursive-delete-race.js diff --git a/lib/internal/fs/recursive_watch.js b/lib/internal/fs/recursive_watch.js index ad12afc9a8f755..ec738536db6deb 100644 --- a/lib/internal/fs/recursive_watch.js +++ b/lib/internal/fs/recursive_watch.js @@ -157,7 +157,9 @@ class FSWatcher extends EventEmitter { } } } catch (error) { - this.emit('error', error); + if (error.code !== 'ENOENT') { + this.emit('error', error); + } } } diff --git a/test/parallel/test-fs-watch-recursive-delete-race.js b/test/parallel/test-fs-watch-recursive-delete-race.js new file mode 100644 index 00000000000000..f60668197c57cd --- /dev/null +++ b/test/parallel/test-fs-watch-recursive-delete-race.js @@ -0,0 +1,44 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const { kFSWatchStart } = require('internal/fs/watchers'); +const { FSWatcher } = require('internal/fs/recursive_watch'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +tmpdir.refresh(); + +const parent = tmpdir.resolve('parent'); +const child = path.join(parent, 'child'); +fs.mkdirSync(child, { recursive: true }); +fs.writeFileSync(path.join(child, 'test.tmp'), 'test'); + +const watch = fs.watch; +let deletedChild = false; +fs.watch = function(filename, ...args) { + const watcher = Reflect.apply(watch, this, [filename, ...args]); + + if (filename === child) { + fs.rmSync(child, { recursive: true }); + deletedChild = true; + } + + return watcher; +}; + +const watcher = new FSWatcher({ recursive: true }); +watcher.on('error', common.mustNotCall()); + +try { + watcher[kFSWatchStart](parent); + assert.strictEqual(deletedChild, true); +} finally { + watcher.close(); + fs.watch = watch; +} From 3dfd5125ac0cf26fd9101807fc65ece634c2fc4a Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:34:34 -0700 Subject: [PATCH 2/5] test: accept SIGILL aborts in async-hooks tests Use common.nodeProcessAborted() for the intentional abort path so platform-specific abort signals such as SIGILL are accepted. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 PR-URL: https://github.com/nodejs/node/pull/63687 Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-06-01.md#jstest-failure Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca --- test/async-hooks/test-emit-after-on-destroyed.js | 2 +- test/async-hooks/test-improper-order.js | 4 ++-- test/async-hooks/test-improper-unwind.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/async-hooks/test-emit-after-on-destroyed.js b/test/async-hooks/test-emit-after-on-destroyed.js index 913cec7bd6a61b..719d6ab91a54fc 100644 --- a/test/async-hooks/test-emit-after-on-destroyed.js +++ b/test/async-hooks/test-emit-after-on-destroyed.js @@ -55,7 +55,7 @@ if (process.argv[2] === 'child') { child.on('close', common.mustCall((code, signal) => { if ((common.isAIX || (common.isLinux && process.arch === 'x64')) && - signal === 'SIGABRT') { + common.nodeProcessAborted(code, signal)) { // XXX: The child process could be aborted due to unknown reasons. Work around it. } else { assert.strictEqual(signal, null); diff --git a/test/async-hooks/test-improper-order.js b/test/async-hooks/test-improper-order.js index f39c79a72bc85c..47678fb19777e3 100644 --- a/test/async-hooks/test-improper-order.js +++ b/test/async-hooks/test-improper-order.js @@ -53,8 +53,8 @@ if (process.argv[2] === 'child') { child.on('close', common.mustCall((code, signal) => { if ((common.isAIX || - (common.isLinux && process.arch === 'x64')) && - signal === 'SIGABRT') { + (common.isLinux && process.arch === 'x64')) && + common.nodeProcessAborted(code, signal)) { // XXX: The child process could be aborted due to unknown reasons. Work around it. } else { assert.strictEqual(signal, null); diff --git a/test/async-hooks/test-improper-unwind.js b/test/async-hooks/test-improper-unwind.js index fb7f212a6804b2..9e651b3bc50f4e 100644 --- a/test/async-hooks/test-improper-unwind.js +++ b/test/async-hooks/test-improper-unwind.js @@ -58,7 +58,7 @@ if (process.argv[2] === 'child') { child.on('close', common.mustCall((code, signal) => { if ((common.isAIX || (common.isLinux && process.arch === 'x64')) && - signal === 'SIGABRT') { + common.nodeProcessAborted(code, signal)) { // XXX: The child process could be aborted due to unknown reasons. Work around it. } else { assert.strictEqual(signal, null); From b345a17095fd95b363573a32ca28996c882d57f2 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 3 Jun 2026 11:03:29 +0200 Subject: [PATCH 3/5] doc: mark Node.js 25 as End-of-Life MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Antoine du Hamel PR-URL: https://github.com/nodejs/node/pull/63692 Reviewed-By: Jithil P Ponnan Reviewed-By: Colin Ihrig Reviewed-By: Richard Lau Reviewed-By: Tierney Cyren Reviewed-By: Luigi Pinca Reviewed-By: Tobias Nießen --- CHANGELOG.md | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e74a59686da8b..465694e6610d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Select a Node.js version below to view the changelog history: * [Node.js 26](doc/changelogs/CHANGELOG_V26.md) **Current** -* [Node.js 25](doc/changelogs/CHANGELOG_V25.md) Current +* [Node.js 25](doc/changelogs/CHANGELOG_V25.md) End-of-Life * [Node.js 24](doc/changelogs/CHANGELOG_V24.md) **Long Term Support** * [Node.js 23](doc/changelogs/CHANGELOG_V23.md) End-of-Life * [Node.js 22](doc/changelogs/CHANGELOG_V22.md) Long Term Support @@ -36,7 +36,6 @@ release. - @@ -48,22 +47,6 @@ release. 26.0.0
-
26 (Current)25 (Current) 24 (LTS) 22 (LTS)
-25.9.0
-25.8.2
-25.8.1
-25.8.0
-25.7.0
-25.6.1
-25.6.0
-25.5.0
-25.4.0
-25.3.0
-25.2.1
-25.2.0
-25.1.0
-25.0.0
-
24.16.0
24.15.0
24.14.1
From f47f941e74f45148fc57f7660261ccec3225e7a4 Mon Sep 17 00:00:00 2001 From: Tian Teng Date: Wed, 3 Jun 2026 17:51:50 +0200 Subject: [PATCH 4/5] crypto: handle cipher context allocation failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Herrtian <70463940+Herrtian@users.noreply.github.com> PR-URL: https://github.com/nodejs/node/pull/63542 Fixes: https://github.com/nodejs/node/issues/62774 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Tobias Nießen --- src/crypto/crypto_aes.cc | 4 +++- src/crypto/crypto_chacha20_poly1305.cc | 4 +++- src/crypto/crypto_cipher.cc | 10 +++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index 9172def7d4ebee..2f8f9571c6d2db 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -48,7 +48,9 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, CHECK_EQ(key_data.GetKeyType(), kKeyTypeSecret); auto ctx = CipherCtxPointer::New(); - CHECK(ctx); + if (!ctx) { + return WebCryptoCipherStatus::FAILED; + } if (params.cipher.isWrapMode()) { ctx.setAllowWrap(); diff --git a/src/crypto/crypto_chacha20_poly1305.cc b/src/crypto/crypto_chacha20_poly1305.cc index cfe43122d5aa55..7393c10f3c51ea 100644 --- a/src/crypto/crypto_chacha20_poly1305.cc +++ b/src/crypto/crypto_chacha20_poly1305.cc @@ -222,7 +222,9 @@ WebCryptoCipherStatus ChaCha20Poly1305CipherTraits::DoCipher( return WebCryptoCipherStatus::OK; #else auto ctx = CipherCtxPointer::New(); - CHECK(ctx); + if (!ctx) { + return WebCryptoCipherStatus::FAILED; + } const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt; diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index dec72c20412e4e..a165e8e3409d6f 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -93,6 +93,11 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { // If it is, then the getCipherInfo will succeed with the given // values. auto ctx = CipherCtxPointer::New(); + if (!ctx) { + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "Failed to allocate cipher context"); + } + if (!ctx.init(cipher, true)) { return; } @@ -338,7 +343,10 @@ void CipherBase::CommonInit(const char* cipher_type, MarkPopErrorOnReturn mark_pop_error_on_return; CHECK(!ctx_); ctx_ = CipherCtxPointer::New(); - CHECK(ctx_); + if (!ctx_) { + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env(), "Failed to allocate cipher context"); + } if (cipher.isWrapMode()) { ctx_.setAllowWrap(); From af2e68bba5a81f795c9e351110145d24789e9361 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Thu, 4 Jun 2026 02:37:13 +0900 Subject: [PATCH 5/5] =?UTF-8?q?doc:=20fix=20"used=20to=20sent"=20=E2=86=92?= =?UTF-8?q?=20"used=20to=20send"=20in=20http2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daijiro Wachi PR-URL: https://github.com/nodejs/node/pull/63700 Reviewed-By: Stephen Belanger Reviewed-By: Tierney Cyren Reviewed-By: Luigi Pinca Reviewed-By: Rafael Gonzaga --- doc/api/http2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 3003a078b2c56d..a73035e66d77ac 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1942,7 +1942,7 @@ server.on('stream', (stream) => { Initiates a response. When the `options.waitForTrailers` option is set, the `'wantTrailers'` event will be emitted immediately after queuing the last chunk of payload data to be sent. The `http2stream.sendTrailers()` method can then be -used to sent trailing header fields to the peer. +used to send trailing header fields to the peer. When `options.waitForTrailers` is set, the `Http2Stream` will not automatically close when the final `DATA` frame is transmitted. User code must call either @@ -2065,7 +2065,7 @@ after a stream has finished is supported. When the `options.waitForTrailers` option is set, the `'wantTrailers'` event will be emitted immediately after queuing the last chunk of payload data to be -sent. The `http2stream.sendTrailers()` method can then be used to sent trailing +sent. The `http2stream.sendTrailers()` method can then be used to send trailing header fields to the peer. When `options.waitForTrailers` is set, the `Http2Stream` will not automatically @@ -2270,7 +2270,7 @@ default behavior is to destroy the stream. When the `options.waitForTrailers` option is set, the `'wantTrailers'` event will be emitted immediately after queuing the last chunk of payload data to be -sent. The `http2stream.sendTrailers()` method can then be used to sent trailing +sent. The `http2stream.sendTrailers()` method can then be used to send trailing header fields to the peer. When `options.waitForTrailers` is set, the `Http2Stream` will not automatically