Bumping Node: what would break

Research only — nothing was changed, built or installed. Registry data and image contents read on 2026-09-14, against the container currently running Node v22.23.2 / npm 10.9.8.

Verdict: safe. Target node:24-bookworm.

Nothing in the image caps Node below 24. OpenClaw is the only package that requires the bump; everything else already allows it. Every native addon in play is N-API, which is the ABI-stable kind — so the usual "native module breaks on a Node major" failure class does not apply here. Staying on bookworm keeps Debian 12 and its Python 3.11, which is what Hermes's venv and the batteries-included Python stack are built against.

Two things to watch, neither a blocker: Hermes pins npm as well as Node and today's 24-bookworm only just clears it, and the whole CLI install section is best-effort || echo "… failed" — which is exactly how OpenClaw has been missing all along. A green build after the bump proves nothing; the build log has to be read.

1. engines, as published

Raw values from npm view <pkg>@<version> engines, so you can check the reading.

packageinstalledengines.nodevs Node 24
openclawabsent (2026.9.4 latest)">=24.16.0 <25 || >=26.1.0"requires bump
@anthropic-ai/claude-code2.1.270">=22.0.0"allows
@google/gemini-cli0.59.0">=20"allows
@openai/codex0.154.0 / 0.152.1">=16"allows
opencode-ai1.18.30field absentunconstrained
ccusage—field absentunconstrained
playwright (chromium step)^1.62.1">=20"allows

Nothing caps Node below 24. The claude-code and codex values are also less load-bearing than they look: both now ship platform binaries (@anthropic-ai/claude-code-linux-x64, @openai/codex-linux-x64), so Node is a launcher shim rather than the runtime. Same for opencode-ai, which ships opencode-linux-x64. Of the npm-installed CLIs, gemini-cli is the one that genuinely runs on the system Node — and it asks only for >=20.

2. The two non-npm CLIs — neither is at risk

Both checked on the running container rather than assumed.

But Hermes is not Node-free at install time, and this is the one new constraint the bump introduces. Its tree contains a full node_modules, and ~/.hermes/hermes-agent/package.json declares:

"engines": { "node": "^22.22.0 || ^24.11.0 || >=26.0.0",
             "npm":  "<11.10.0 || >=11.17.0" }

Node 24 is allowed. The npm range is the interesting half, because the base image decides it for you:

Nodebundled npmvs Hermes's npm range
v22.23.2 (today)10.9.8ok — under 11.10.0
v24.21.0 (24-bookworm today)11.19.0ok — over 11.17.0
v24.16.0 (OpenClaw's floor)11.13.0inside the excluded window
v26.8.211.19.1ok

Today's floating 24-bookworm clears it with two minor versions to spare. It would not have, three releases ago. If you want that to stay true without watching it, pin node:24.21-bookworm — the Dockerfile already carries a scar from a floating tag in the Playwright/chromium comment.

3. Agent Manager's own server

server/package.json declares "engines": { "node": ">=20.19" } — advisory only; there is no .npmrc with engine-strict anywhere in the repo, so engines produce warnings, not install failures. web/package.json declares none. There is no .nvmrc and no CI job pinning a Node version, so nothing else in the repo needs editing alongside the Dockerfile.

Native dependencies — all N-API, which is why this is low risk

Compiled addons are what actually break across Node majors, and only if they use NAN/V8 APIs. Neither of these does.

dependencykindevidence
node-pty ^1.0.0 → 1.1.0N-API, compiled in-imagedepends on node-addon-api ^7.1.0; src/unix/pty.cc includes napi.h, not nan.h. Install is node scripts/prebuild.js || node-gyp rebuild, and make/g++ are in the image, so it recompiles against whatever headers ship.
@coder/libghostty-vt-node 0.1.0-beta.0N-API, prebuiltnode-addon-api ^8.3.1 + node-gyp-build ^4.8.4, with prebuilds/linux-x64/ present. N-API prebuilds are per-platform, not per-Node-version.
sqlite-vec (OpenClaw's optional dep)prebuilt platform packagessqlite-vec-linux-x64 etc. — a loadable SQLite extension, not a Node ABI addon.

There is no better-sqlite3 and no node-gyp module using the V8 API anywhere in the dependency set.

The SQLite you asked me to look at first is Node's own

The durable-state / trace work does not use an npm SQLite package. It uses the built-in node:sqlite, imported defensively in two places so a Node without it degrades rather than crashes:

server/src/traces.js:514  try { ({ DatabaseSync } = await import('node:sqlite')); } catch { /* older node: skip opencode */ }
server/src/share.js:362   try { ({ DatabaseSync } = await import('node:sqlite')); } catch { return null; }

The API surface used is small and read-only — new DatabaseSync(path, { readOnly: true }), .prepare(), .get(), .all(), one .exec(). That is the original core of the module and is unchanged from 22.5 onward. It runs unflagged today; nothing in entrypoint.sh or the package scripts passes --experimental-sqlite or suppresses warnings. The module remains experimental on 24, so expect the same ExperimentalWarning on stderr, and treat the opencode/hermes trace suites as the thing to re-run rather than assuming.

4. Which Node, and why 24

lineneweststatusOpenClaw range
2424.21.0 (2026-09-07)Active LTS "Krypton"satisfied (>=24.16.0 <25)recommended
2525.9.0 (2026-03-31)not LTS, staleexplicitly excludedno
2626.8.2 (2026-09-09)Current, not LTSsatisfied (>=26.1.0)viable, unnecessary

Take Node 24. It is the LTS line, it satisfies OpenClaw with five patch releases of headroom above the 24.16.0 floor, and it is the smaller jump. Node 26 would also work but buys nothing here and puts the fleet on a non-LTS line that will churn. Node 25 is excluded by OpenClaw and is effectively dead anyway — last release was March.

node:24-bookworm exists on Docker Hub (as do lts-bookworm and krypton-bookworm, which alias it). Keeping bookworm is the single biggest blast-radius reducer: the Debian base, its python3 3.11, apt package set, and glibc all stay exactly as they are. Hermes's venv and the /opt/py data stack are built on that Python — moving to a trixie-based tag would rebuild both against a different interpreter for no reason.

Both FROM lines change (Dockerfile:2 web builder, :11 runtime). The comment on :10 — "Node 22+: required by OpenClaw (22.19+)" — is stale and should be corrected while you are there.

5. Wider blast radius

6. What to check after the build

  1. openclaw --version — the whole point; it should exist for the first time.
  2. claude, codex, gemini, opencode, hermes, fx, ccusage all still answer --version.
  3. Grep the build log for install failed and for the Hermes step — a green image is not evidence.
  4. server/node_modules/node-pty/build/Release/pty.node exists, and terminal panes actually render (that also exercises the libghostty prebuild).
  5. Open an opencode or hermes session in the Reader — that is the node:sqlite path. The server suites test/reader-protocol, test/trace-window and test/trace-tail cover it; run them individually, since the runner stops at the first failure and test/crons.test.mjs #4 is known-red on main.
  6. npm --version inside the image, against Hermes's excluded window of 11.10.0–11.16.x.

7. What I could not determine without building

Stated rather than guessed, with the test for each:

Sources: npm registry (npm view … engines), nodejs.org/dist/index.json for release lines and bundled npm, Docker Hub tag list, and direct inspection of the running container and a fresh read-only clone at a74b327. No file on the box was modified.