zed-industries/zed · error
node at {} is too old. want: {}, got: {}
Error message
node at {} is too old. want: {}, got: {} What it means
After `node --version` succeeds, SystemNodeRuntime parses the version and enforces a floor of 22.0.0. Older system Nodes bail with the wanted and found versions. The check exists because Zed's bundled extensions/tooling require modern Node APIs; the runtime then falls back to using the data-dir layout for caches.
Source
Thrown at crates/node_runtime/src/node_runtime.rs:877
impl SystemNodeRuntime {
const MIN_VERSION: semver::Version = Version::new(22, 0, 0);
async fn new(node: PathBuf, npm: PathBuf) -> Result<Self> {
let output = util::command::new_command(&node)
.arg("--version")
.output()
.await
.with_context(|| format!("running node from {:?}", node))?;
if !output.status.success() {
anyhow::bail!(
"failed to run node --version. stdout: {}, stderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
let version_str = String::from_utf8_lossy(&output.stdout);
let version = semver::Version::parse(version_str.trim().trim_start_matches('v'))?;
if version < Self::MIN_VERSION {
anyhow::bail!(
"node at {} is too old. want: {}, got: {}",
node.to_string_lossy(),
Self::MIN_VERSION,
version
)
}
let scratch_dir = paths::data_dir().join("node");
fs::create_dir(&scratch_dir).await.ok();
_ = fs::remove_dir_all(scratch_dir.join("cache")).await;
fs::create_dir(scratch_dir.join("cache")).await.ok();
Ok(Self {
node,
npm,
scratch_dir,
})
}View on GitHub (pinned to 9d272b0363)
Solutions
- Upgrade system Node to >=22 (e.g. `nvm install 22 && nvm alias default 22`, or install from nodejs.org)
- Prepend the new Node's bin directory to PATH so Zed resolves it first
- Or remove/renumber the old node from PATH so Zed's bundled downloaded runtime is used instead
Example fix
# before $ node --version v18.20.4 # after $ nvm install 22 && nvm alias default 22 $ node --version v22.x.y
Defensive patterns
Strategy: validation
Validate before calling
fn node_new_enough(version_output: &str) -> bool {
version_output
.trim()
.trim_start_matches('v')
.parse::<semver::Version>()
.map_or(false, |v| v >= semver::Version::new(22, 0, 0))
} Type guard
fn meets_min_node_version(v: &semver::Version) -> bool {
*v >= semver::Version::new(22, 0, 0)
} Try / catch
if version < SystemNodeRuntime::MIN_VERSION {
// report and skip the system runtime; use the bundled one instead
return NodeRuntime::install_if_needed(&http).await;
} Prevention
- Keep the default system node >= 22 (set nvm default, update distro package)
- CI images: use node:22-or-newer tags where Zed runs
- Prefer the bundled runtime in locked-down environments over stale system nodes
When it happens
Trigger: A system node older than v22 (18.x LTS, 20.x, ancient 16.x from distro packages) is picked up when Zed prefers the system runtime.
Common situations: Distros shipping node 18/20 as `nodejs`; stale nvm default (`nvm alias default 18`); CI images pinned to node:20; WSL environments with an old apt-installed Node.
Related errors
- failed to run node --version. stdout: {}, stderr: {}
- Default prettier is not installed and cannot be started
- Cannot start default prettier due to its installation failur
- mismatched versions: ({}) != ({})
- {}
AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20).
Data as JSON: /api/errors/dfb458f8982769d5.
Report an issue: GitHub.