Skip to main content

SvelteKit 3.0 landed on 1 October 2026, and the announcement sets a gentle tone: everything will feel very familiar, the same framework with a little more polish and a little less junk. The migration guide is short and sv migrate does most of the work. All true, and none of it is where your upgrade will fail.

We migrated a SvelteKit 2 app to 3 and scaffolded a fresh 3 app alongside it. The code migration was excellent. The install was not. A brand new project created by the official CLI cannot be installed by the npm that ships with the Node version SvelteKit 3 declares as its own minimum.

TL;DR

  • npm install crashes on a freshly scaffolded SvelteKit 3 app with TypeError: Cannot read properties of null (reading 'edgesOut') inside npm’s dependency resolver. Reproduced on seven npm versions, 10.8.2 through 11.5.0.
  • The behaviour changes at npm 11.6.0. Node 20 and Node 22 have never shipped an npm that recent. SvelteKit 3 requires node >=22.17, and Node 22.17.0 ships npm 10.9.2.
  • CI will not warn you. npm ci against a committed lockfile installs cleanly on the same broken npm, so pipelines stay green while new developers cannot onboard.
  • TypeScript 7 is excluded. SvelteKit 3 peers typescript@^6.0.0 and svelte-check peers ^5.0.0 || ^6.0.0. Installing TypeScript 7.0.2, current since 8 July 2026, is a hard ERESOLVE.
  • Vite 8 readiness, the thing we expected to break, is fine. All 14 packages we swept that declare a Vite peer accept Vite 8. We were wrong about where the risk was, and that is the useful part.

What we actually ran

Two projects from the official CLI, both carrying the add-ons a real client project does: Prettier, ESLint, Vitest, Tailwind, an adapter and enhanced-img. Host: Node v22.22.0, npm 10.9.4, Linux.

npx sv@1.0.1 create app3 --template demo --types ts \
  --add prettier eslint vitest="usages:unit" \
  tailwindcss="plugins:none" sveltekit-adapter="adapter:auto" --install npm

npx sv@0.17.1 create app2 ...   # SvelteKit 2.63.0, then:
npx sv@1.0.1 migrate sveltekit-3 --tasks all --confirm

Every result below came from running the install, not reading a range.

The scaffold does not install

The first command fails before it finishes:

â—‡  Installing dependencies with npm...
â–   Failed to install dependencies
│  npm error Cannot read properties of null (reading 'edgesOut')

The debug log puts the crash in #loadPeerSet at @npmcli/arborist/lib/arborist/build-ideal-tree.js:1289, with the last unfinished timer being idealTree:node_modules/vitest. Removing Vitest installs 193 packages without complaint, which narrows it to the Vitest peer graph. We then ran the same project through fourteen package manager versions:

Package manager Result
npm 10.8.2, 10.9.2, 10.9.4, 10.9.9 crash
npm 11.0.0, 11.4.0, 11.5.0 crash
npm 11.6.0 (3 Sept 2025) and every later 11.x tried installs
pnpm 12.8.1, yarn 1.22.22 installs

Two other things install cleanly on the broken npm: pinning vitest@^5 instead of the ^4.1.8 the CLI writes, and passing --legacy-peer-deps. Three one-line fixes, and nothing in the release notes points at any of them.

Why this reaches you rather than staying a curiosity

SvelteKit 3 sets engines: { node: ">=22.17" }. We pulled the Node release index and checked which npm each release bundles. Node 20 tops out at npm 10.8.2. Node 22, the Jod LTS line SvelteKit 3 nominates as its floor, runs from npm 10.5.1 to 10.9.9 in v22.23.3 of 23 September 2026. Node 24 did not carry a fixed npm until v24.8.0. Node 26.0.0 ships npm 11.12.1.

The framework’s declared minimum Node version has never, across its entire release history, shipped an npm that can install the framework’s own starter project. A team that does exactly what the documentation says, installs Node 22 LTS and runs sv create, hits a stack trace with no actionable message.

The bug is not new. npm/cli issue #9787 has been open since 21 July 2026 with the identical stack frame, and #9960 was filed on 9 September by a user on npm 10.9.4. It is fixed on the npm 11 line and never backported to npm 10, which shipped a release on 29 July 2026.

Your pipeline will stay green

We deleted node_modules, kept the lockfile a colleague’s npm 11 had written, and ran npm ci on npm 10.9.4. It installed 208 packages in five seconds.

npm ci reads the lockfile and never builds an ideal tree from scratch, so it never enters the code path that crashes. Your CI is therefore the last place this will surface. It shows up when somebody clones without a lockfile, when a dependency bump triggers a real resolve, when a Docker build runs npm install rather than npm ci, and on the laptop of whoever joined this week. That is a support burden arriving as confused individual messages, not a red build.

The workaround a search result will hand you, --legacy-peer-deps, also changes what you get. Diffing two lockfiles built from the same manifest, the npm 11 tree hoists yaml@2.9.1 and keeps a nested yaml@1.10.3; the --legacy-peer-deps tree promotes yaml@1.10.3 to the top level and drops the nested copy. One major version of a transitive dependency, silently different, with no advisory on either today. The fix most teams reach for changes resolution in a way nobody reviews, and the lockfile gets committed.

TypeScript 7 is not invited

TypeScript 7.0.2 has been the published latest since 8 July 2026, and the last 6.x release was 6.0.3 in April. Yet sv create writes "typescript": "^6.0.3" into a project scaffolded in October, and the peer ranges make that mandatory rather than conservative. Bumping the manifest to ^7.0.0:

npm error ERESOLVE unable to resolve dependency tree
npm error Found: typescript@7.0.2
npm error Could not resolve dependency:
npm error peer typescript@"^5.0.0 || ^6.0.0" from svelte-check@4.7.6

@sveltejs/kit@3.0.0 peers typescript@^6.0.0 and @sveltejs/package@3.0.0 does the same, while typescript-eslint@8.71.0 carries a hard upper bound of >=4.8.4 <6.1.0. If your team has already moved to TypeScript 7, SvelteKit 3 is a step backwards on the compiler, enforced by svelte-check via the check script the CLI generated for you.

Where we were wrong, and the pins that do bite

We went in expecting a Vite 8 ecosystem problem. There isn’t one. Of 35 packages swept against the npm registry, 14 declare a Vite peer range and all 14 accept Vite 8.3.2, including Vitest, Storybook and Tailwind. The reason is mundane: SvelteKit 2.63.0 already scaffolds with vite@^8.0.16, so for anyone current on 2.x the Vite major is not part of this upgrade at all.

What does break at install time is third-party code with a hard @sveltejs/kit peer pin. vite-plugin-kit-routes@1.0.6 peers ^2.4.0, @vite-pwa/sveltekit@1.1.0 peers ^1.3.1 || ^2.0.1, and @sveltejs/amp@1.1.5 peers ^1.0.0 || ^2.0.0. Each is an ERESOLVE the moment kit goes to 3, while @sentry/sveltekit@11.2.0 shipped 2.x || ^3.0.0-0 ahead of the release. Grep your manifest for anything peering on kit directly before you schedule the work.

The code migration is genuinely good

None of this is a criticism of the migration, which deserves credit. sv migrate sveltekit-3 ran twelve tasks and changed twelve files for a net 42 insertions and 44 deletions: svelte.config.js folded into vite.config.ts, every $lib import rewritten to the #lib subpath form with the matching imports field added, tsconfig.json collapsed to extend $app/tsconfig. It then wrote a MIGRATION_TASKS.md naming the exact files for the three things it would not automate. Afterwards svelte-check reported 332 files and zero errors, the build completed in 656ms and the tests passed. The one thing it never touches is vitest or typescript, so it hands you back a project in precisely the combination that cannot be installed.

What to do before you schedule this

  1. Pin the package manager, not just Node. Set a packageManager field and an npm 11.6.0 floor in CI and onboarding. Node 22 LTS plus its bundled npm is now unsupported for this framework.
  2. Reproduce it once, deliberately. Delete node_modules and the lockfile, then run npm install. A green pipeline is evidence about npm ci, nothing more.
  3. Decide the Vitest question explicitly. Moving to vitest@^5 sidesteps the crash and is itself a major upgrade. Choose it rather than discovering it.
  4. Audit typescript first if your estate is on 7.x, because this upgrade pins you to 6.
  5. Treat --legacy-peer-deps as a change, not a flag. If someone uses it, diff the lockfile before it merges.

The broader lesson is not about Svelte. Framework teams have become very good at migrating code, and codemods plus an agent will now carry most of a major version bump. The residual risk has moved down the stack into resolver versions, peer ranges and the package manager your base image happens to bundle, and none of that appears in a migration guide.

Everything here is reproducible in about ten minutes and pinned to 2 October 2026. If you are weighing a SvelteKit 3 migration, a Node and npm baseline across a client estate, or a CI pipeline quietly testing a different tree to the one your developers have, get in touch.

📷 Photo by Steve A Johnson on Unsplash