Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Windows: externals plugin bundles a package imported by absolute path, duplicating it (`pinia` → "no active Pinia" on Nuxt 4.6)

Open Beginner friendly
#4,761 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
83/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
node.js, typescript
Domain
backend, devtools

Research direction

Start in Nitro's externals implementation at the pkgName !== originalId branch described in the issue, and inspect the two path comparisons against id. Reproduce the Windows slash mismatch with the reported Nuxt and Pinia setup, then verify that the fix externalizes the absolute-path import and the SSR request returns 200 with count: 1.

Written by the indexing model from the issue text.

Description

bug externals nuxt v2 windows
Environment
  • OS: Windows 11 (x64)
  • Node.js 24.21.0, pnpm 12.11.2
  • nitropack 2.13.4 (node-server preset)
  • Nuxt 4.6.1 (also 4.6.0), Vite 8.3.4, Vue 3.5.43
  • pinia 4.0.3, @pinia/nuxt 1.0.2
Reproduction

A fresh Nuxt app with one Pinia store. No other modules or config.

package.json

{
  "private": true,
  "type": "module",
  "scripts": { "build": "nuxt build", "preview": "node .output/server/index.mjs" },
  "dependencies": {
    "@pinia/nuxt": "1.0.2",
    "nuxt": "4.6.1",
    "pinia": "4.0.3",
    "vue": "3.5.43"
  }
}

nuxt.config.ts

export default defineNuxtConfig({
  compatibilityDate: '2026-10-01',
  modules: ['@pinia/nuxt'],
})

app/stores/counter.ts

import { defineStore } from 'pinia'

export const useCounter = defineStore('counter', {
  state: () => ({ count: 1 }),
})

app/pages/index.vue

<script setup lang="ts">
import { useCounter } from '../stores/counter'

const counter = useCounter()
</script>

<template>
  <p>count: {{ counter.count }}</p>
</template>

app/app.vue

<template>
  <NuxtPage />
</template>

Steps (on Windows):

pnpm install
pnpm build
node .output/server/index.mjs
curl http://localhost:3000/
Describe the bug

Expected

200 with <p>count: 1</p>.

Actual

500 on every SSR request:

[🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?

nuxt dev is not affected. The same app on Nuxt 4.5.2 (same Nitro 2.13.4) renders fine.

Additional context

Cause

The Vite SSR bundle (.nuxt/dist/server/server.mjs) imports pinia twice, by two different specifiers:

import { createPinia, setActivePinia, shouldHydrate } from "C:/…/node_modules/.pnpm/[email protected]_…/node_modules/pinia/dist/pinia.js" // from @pinia/nuxt's plugin
import { defineStore } from "pinia"                                                                                                 // from the app's store

Nitro's externals plugin keeps the bare pinia external, but bundles the absolute-path import into chunks/virtual/entry.mjs. The server then runs two pinia module instances: the plugin activates one, the store reads the other.

It bundles it because of this check in externals (dist/rollup/index.mjs, the pkgName !== originalId branch for an absolute originalId):

const packageEntry = tryResolve(pkgName, importer);
if (packageEntry !== id) {
  const guessedSubpath = await lookupNodeModuleSubpath(id).catch(() => null);
  const resolvedGuess = guessedSubpath && tryResolve(join(pkgName, guessedSubpath), importer);
  if (resolvedGuess === id) {
    // … external
  }
  return null; // → bundled
}

id is normalize(originalId), so it uses forward slashes. tryResolve returns fileURLToPath(...), which uses backslashes on Windows. The two strings name the same file but can never be equal. Logged values for the pinia import above:

{
  "id":           "C:/…/node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/pinia/dist/pinia.js",
  "packageEntry": "C:\\…\\node_modules\\.pnpm\\[email protected]_@[email protected][email protected]\\node_modules\\pinia\\dist\\pinia.js"
}

So on Windows, with the default externals settings (tracing on, no matching inline/external rule), a package imported by absolute path is bundled instead of externalized. It only breaks at runtime when the same package is also imported by its bare name somewhere else and keeps module-level state, as pinia does.

Fix (verified locally)

Normalize before comparing. With this change the repro builds a single external pinia (no createPinia in entry.mjs) and GET / returns 200 with count: 1:

-        if (packageEntry !== id) {
+        if (normalize(packageEntry || "") !== id) {
           const guessedSubpath = await lookupNodeModuleSubpath(id).catch(() => null);
           const resolvedGuess = guessedSubpath && tryResolve(join(pkgName, guessedSubpath), importer);
-          if (resolvedGuess === id) {
+          if (resolvedGuess && normalize(resolvedGuess) === id) {

(normalize is already imported from pathe in that file.)

Related

  • nuxt/nuxt#36467 and nitrojs/nitro#4732 are the same class of bug: string inline rules versus backslash ids. #4732 normalizes the id only inside the string matcher, so it does not cover this comparison.
  • Nuxt-side context: on 4.6 the @pinia/nuxt plugin's pinia import reaches Nitro as an absolute path (on 4.5.2 it is bare "pinia"), which is what exposes this. Absolute imports of other packages (hookable, unctx, ufo, …) already occur on 4.5.2.
  • Workaround: nitro.externals.inline: ['pinia', /[\\/]node_modules[\\/]pinia[\\/]/] (bundles both imports into one copy).
Dominant language
TypeScript
Stars
11.3k
Forks
936
Avg merge
1d 11h
Merged PRs (30d)
170

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from nitrojs/nitro

All issues in nitrojs/nitro

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.