From 2ca78a8aed1a9f153a97066ad713edfa7f886169 Mon Sep 17 00:00:00 2001 From: Glucksberg <80581902+Glucksberg@users.noreply.github.com> Date: Thu, 5 Feb 2026 17:42:52 -0400 Subject: [PATCH] fix(runtime): bump minimum Node.js version to 22.12.0 (#5370) * fix(runtime): bump minimum Node.js version to 22.12.0 Aligns the runtime guard with the declared package.json engines requirement. The Matrix plugin (and potentially others) requires Node >= 22.12.0, but the runtime guard previously allowed 22.0.0+. This caused confusing errors like 'Cannot find module @vector-im/matrix-bot-sdk' when the real issue was an unsupported Node version. - Update MIN_NODE from 22.0.0 to 22.12.0 - Update error message to reflect the correct version - Update tests to use 22.12.0 as the minimum valid version Fixes #5292 * fix: update test versions to match MIN_NODE=22.12.0 --------- Co-authored-by: Markus Glucksberg --- src/daemon/runtime-paths.test.ts | 11 +++++++---- src/infra/runtime-guard.test.ts | 17 +++++++++++------ src/infra/runtime-guard.ts | 4 ++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/daemon/runtime-paths.test.ts b/src/daemon/runtime-paths.test.ts index d7ec4d6048f..432bb55a681 100644 --- a/src/daemon/runtime-paths.test.ts +++ b/src/daemon/runtime-paths.test.ts @@ -30,7 +30,8 @@ describe("resolvePreferredNodePath", () => { throw new Error("missing"); }); - const execFile = vi.fn().mockResolvedValue({ stdout: "22.1.0\n", stderr: "" }); + // Node 22.12.0+ is the minimum required version + const execFile = vi.fn().mockResolvedValue({ stdout: "22.12.0\n", stderr: "" }); const result = await resolvePreferredNodePath({ env: {}, @@ -51,7 +52,8 @@ describe("resolvePreferredNodePath", () => { throw new Error("missing"); }); - const execFile = vi.fn().mockResolvedValue({ stdout: "18.19.0\n", stderr: "" }); + // Node 22.11.x is below minimum 22.12.0 + const execFile = vi.fn().mockResolvedValue({ stdout: "22.11.0\n", stderr: "" }); const result = await resolvePreferredNodePath({ env: {}, @@ -92,7 +94,8 @@ describe("resolveSystemNodeInfo", () => { throw new Error("missing"); }); - const execFile = vi.fn().mockResolvedValue({ stdout: "22.0.0\n", stderr: "" }); + // Node 22.12.0+ is the minimum required version + const execFile = vi.fn().mockResolvedValue({ stdout: "22.12.0\n", stderr: "" }); const result = await resolveSystemNodeInfo({ env: {}, @@ -102,7 +105,7 @@ describe("resolveSystemNodeInfo", () => { expect(result).toEqual({ path: darwinNode, - version: "22.0.0", + version: "22.12.0", supported: true, }); }); diff --git a/src/infra/runtime-guard.test.ts b/src/infra/runtime-guard.test.ts index 1e3d4ef223d..b9ffb2af52c 100644 --- a/src/infra/runtime-guard.test.ts +++ b/src/infra/runtime-guard.test.ts @@ -16,13 +16,16 @@ describe("runtime-guard", () => { }); it("compares versions correctly", () => { - expect(isAtLeast({ major: 22, minor: 0, patch: 0 }, { major: 22, minor: 0, patch: 0 })).toBe( + expect(isAtLeast({ major: 22, minor: 12, patch: 0 }, { major: 22, minor: 12, patch: 0 })).toBe( true, ); - expect(isAtLeast({ major: 22, minor: 1, patch: 0 }, { major: 22, minor: 0, patch: 0 })).toBe( + expect(isAtLeast({ major: 22, minor: 13, patch: 0 }, { major: 22, minor: 12, patch: 0 })).toBe( true, ); - expect(isAtLeast({ major: 21, minor: 9, patch: 0 }, { major: 22, minor: 0, patch: 0 })).toBe( + expect(isAtLeast({ major: 22, minor: 11, patch: 0 }, { major: 22, minor: 12, patch: 0 })).toBe( + false, + ); + expect(isAtLeast({ major: 21, minor: 9, patch: 0 }, { major: 22, minor: 12, patch: 0 })).toBe( false, ); }); @@ -30,11 +33,12 @@ describe("runtime-guard", () => { it("validates runtime thresholds", () => { const nodeOk: RuntimeDetails = { kind: "node", - version: "22.0.0", + version: "22.12.0", execPath: "/usr/bin/node", pathEnv: "/usr/bin", }; - const nodeOld: RuntimeDetails = { ...nodeOk, version: "21.9.0" }; + const nodeOld: RuntimeDetails = { ...nodeOk, version: "22.11.0" }; + const nodeTooOld: RuntimeDetails = { ...nodeOk, version: "21.9.0" }; const unknown: RuntimeDetails = { kind: "unknown", version: null, @@ -43,6 +47,7 @@ describe("runtime-guard", () => { }; expect(runtimeSatisfies(nodeOk)).toBe(true); expect(runtimeSatisfies(nodeOld)).toBe(false); + expect(runtimeSatisfies(nodeTooOld)).toBe(false); expect(runtimeSatisfies(unknown)).toBe(false); }); @@ -73,7 +78,7 @@ describe("runtime-guard", () => { const details: RuntimeDetails = { ...detectRuntime(), kind: "node", - version: "22.0.0", + version: "22.12.0", execPath: "/usr/bin/node", }; expect(() => assertSupportedRuntime(runtime, details)).not.toThrow(); diff --git a/src/infra/runtime-guard.ts b/src/infra/runtime-guard.ts index c15668ebf59..1a56e48abbc 100644 --- a/src/infra/runtime-guard.ts +++ b/src/infra/runtime-guard.ts @@ -9,7 +9,7 @@ type Semver = { patch: number; }; -const MIN_NODE: Semver = { major: 22, minor: 0, patch: 0 }; +const MIN_NODE: Semver = { major: 22, minor: 12, patch: 0 }; export type RuntimeDetails = { kind: RuntimeKind; @@ -88,7 +88,7 @@ export function assertSupportedRuntime( runtime.error( [ - "openclaw requires Node >=22.0.0.", + "openclaw requires Node >=22.12.0.", `Detected: ${runtimeLabel} (exec: ${execLabel}).`, `PATH searched: ${details.pathEnv}`, "Install Node: https://nodejs.org/en/download",