Skip to content

[Bug]: CLI rejects every Node.js prerelease build #36143

Description

@Xevion

Describe the bug

The Storybook CLI can't tell prerelease Node builds from old ones, and it's blocking storybook info when it shouldn't.

The bug is in how the CLI checks your Node version. In code/core/src/bin/dispatcher.ts, it takes process.versions.node, splits it on the dots, and converts each piece to a number:

const [major, minor, patch] = process.versions.node.split('.').map(Number);
return satisfies(`${major}.${minor}.${patch}`, supportedRange);

Checked against Storybook's supported range (>=20.19.0 <22.0.0 || >=22.12.0), here's what that produces on various Node versions:

"18.20.4"                       => [18, 20, 4]    => false   (too old, correctly rejected)
"20.11.1"                       => [20, 11, 1]    => false   (below 20.19.0, correctly rejected)
"20.19.0"                       => [20, 19, 0]    => true    (correctly accepted)
"20.20.2"                       => [20, 20, 2]    => true    (correctly accepted)
"22.9.0"                        => [22, 9, 0]     => false   (below 22.12.0, correctly rejected)
"22.22.2"                       => [22, 22, 2]    => true    (correctly accepted)
"24.15.0"                       => [24, 15, 0]    => true    (correctly accepted)
"26.1.0"                        => [26, 1, 0]     => true    (correctly accepted)
"26.1.0-rc.0"                   => [26, 1, NaN]   => false   (misfire: should be true)
"24.15.0-nightly20260415abcdef" => [24, 15, NaN]  => false   (misfire: should be true)
"27.0.0-alpha.1"                => [27, 0, NaN]   => false   (misfire: should be true)

Any version with a tag after the patch number turns that number into NaN. The rebuilt version string becomes unparseable by semver, satisfies() returns false, and the CLI reports a perfectly supported Node version as too old.

Since this check runs before run() is even defined, it applies to every subcommand, including storybook info. On an affected machine, there's no way to run that command to generate the debug output this issue asks for.

Reproduction link

https://github.com/Xevion/storybook-repro-version-parsing

Reproduction steps

  1. git clone
  2. Build with Docker. The bug is demonstrateable with no components or code, so, the repository is just the Dockerfile.
docker build -t sb-node-prerelease .
docker run --rm sb-node-prerelease

Alternatively, you can test on Linux in just a few seconds by pasting these commands.

curl -sSLO https://nodejs.org/download/rc/v26.1.0-rc.0/node-v26.1.0-rc.0-linux-x64.tar.xz
tar xf node-v26.1.0-rc.0-linux-x64.tar.xz
PATH="$PWD/node-v26.1.0-rc.0-linux-x64/bin:$PATH" npx -y storybook@latest info
# rm -rf ./node-v26.1.0-rc.0-linux-x64{/,.tar.xz}

Produces the following for me:

❯ curl -sSLO https://nodejs.org/download/rc/v26.1.0-rc.0/node-v26.1.0-rc.0-linux-x64.tar.xz
  tar xf node-v26.1.0-rc.0-linux-x64.tar.xz
  PATH="$PWD/node-v26.1.0-rc.0-linux-x64/bin:$PATH" npx -y storybook@latest info
  rm -rf node-v26.1.0-rc.0-linux-x64{/,.tar.xz}
npm warn cli npm v11.13.0 does not support Node.js v26.1.0-rc.0. This version of npm supports the following node versions: `^20.17.0 || >=22.9.0`. You can find the latest version at https://nodejs.org/.
│
■  To run Storybook, you need Node.js version 20.19+ or 22.12+.
│  You are currently running Node.js v26.1.0-rc.0. Please upgrade your Node.js
│  installation.

System

│  Storybook Environment Info:
│
│  System:
│  OS: Linux 7.0 Pop!_OS 22.04 LTS
│  CPU: (16) x64 AMD Ryzen 7 7800X3D 8-Core Processor
│  Shell: 4.2.1 - /home/linuxbrew/.linuxbrew/bin/fish
│  Binaries:
│  Node: 24.19.0 - /home/xevion/.local/share/mise/installs/node/24/bin/node
│  npm: 12.0.2 - /home/xevion/.local/share/mise/installs/npm/latest/package/bin/npm
│  <----- active
│  pnpm: 11.24.0 - /home/xevion/.local/share/mise/installs/pnpm/latest/pnpm
│  Browsers:
│  Chrome: 149.0.7827.53

(Collected after pinning Node to 24, since `storybook info` is itself blocked by the bug.)

Additional context

Passing the raw string to semver instead of reassembling it looks like it would do:

satisfies(process.versions.node, supportedRange, { includePrerelease: true });

includePrerelease is needed because semver otherwise excludes prereleases from ranges that don't name them. It does not loosen the gate:

version plain includePrerelease
26.1.0-rc.0 false true
26.8.0-alpha.0.0.0 false true
27.0.0-nightly20260902 false true
24.19.0 true true
22.11.0 false false
20.18.0 false false

Genuinely unsupported versions still fail.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugclicoresev:S3Medium priority. Fix within months if possible.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions