Skip to content

[clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

Description

@ericcorriel

Preliminary Checks

Reproduction

https://github.com/ericcorriel/clerk-nuxt-bug

Publishable key

pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

Description

Repro repo

https://github.com/ericorriel/clerk-nuxt-bug

Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

Summary

When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

Reproduced on

PackageVersions tested
nuxt4.3.1, 4.4.4 (latest stable)
@clerk/nuxt2.2.8, 2.2.9 (latest stable)
@nuxt/nitro-server4.3.1, 4.4.4
unimport5.6.0, 5.7.0, 6.2.0 (latest)
h31.15.6

The bug is consistent across all combinations above.

Stack trace

ReferenceError: H3Error is not defined
at /path/to/.nuxt/dev/index.mjs:7673:12
at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)

The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

Diagnosis

Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

@nuxt/nitro-server registers these as auto-imports via:

// node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

What we tried (none of these worked)

  1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
  2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
  3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
  4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
  5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
  6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

Workaround that worked

  1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
  2. Hand-roll a Nitro middleware using @clerk/backend directly:
// server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

Hypothesis

Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

Environment

  • macOS 26 (Apple Silicon)
  • Node.js (latest LTS)
  • yarn 1.22.22
  • Nuxt 4.4.4 with Vite/Nitro defaults
  • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

Impact

For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

Environment

- macOS 26 (Apple Silicon)
- Node.js (latest LTS)
- yarn 1.22.22
- Nuxt 4.4.4 with Vite/Nitro defaults
- Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

Metadata

Metadata

Assignees

Labels

needs-triageA ticket that needs to be triaged by a team member

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
    Skip to content

    [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

    Description

    @ericcorriel

    Preliminary Checks

    Reproduction

    https://github.com/ericcorriel/clerk-nuxt-bug

    Publishable key

    pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

    Description

    Repro repo

    https://github.com/ericorriel/clerk-nuxt-bug

    Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

    The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

    Summary

    When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

    Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

    Reproduced on

    PackageVersions tested
    nuxt4.3.1, 4.4.4 (latest stable)
    @clerk/nuxt2.2.8, 2.2.9 (latest stable)
    @nuxt/nitro-server4.3.1, 4.4.4
    unimport5.6.0, 5.7.0, 6.2.0 (latest)
    h31.15.6

    The bug is consistent across all combinations above.

    Stack trace

    ReferenceError: H3Error is not defined
    at /path/to/.nuxt/dev/index.mjs:7673:12
    at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
    

    The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

    Diagnosis

    Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

    const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

    But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

    import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

    So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

    @nuxt/nitro-server registers these as auto-imports via:

    // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
    presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

    This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

    What we tried (none of these worked)

    1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
    2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
    3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
    4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
    5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
    6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

    Workaround that worked

    1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
    2. Hand-roll a Nitro middleware using @clerk/backend directly:
    // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
    secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

    This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

    With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

    Hypothesis

    Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

    I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

    Environment

    • macOS 26 (Apple Silicon)
    • Node.js (latest LTS)
    • yarn 1.22.22
    • Nuxt 4.4.4 with Vite/Nitro defaults
    • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

    Impact

    For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

    A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

    Environment

    - macOS 26 (Apple Silicon)
    - Node.js (latest LTS)
    - yarn 1.22.22
    - Nuxt 4.4.4 with Vite/Nitro defaults
    - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

    Metadata

    Metadata

    Assignees

    Labels

    needs-triageA ticket that needs to be triaged by a team member

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
      Skip to content

      [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

      Description

      @ericcorriel

      Preliminary Checks

      Reproduction

      https://github.com/ericcorriel/clerk-nuxt-bug

      Publishable key

      pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

      Description

      Repro repo

      https://github.com/ericorriel/clerk-nuxt-bug

      Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

      The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

      Summary

      When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

      Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

      Reproduced on

      PackageVersions tested
      nuxt4.3.1, 4.4.4 (latest stable)
      @clerk/nuxt2.2.8, 2.2.9 (latest stable)
      @nuxt/nitro-server4.3.1, 4.4.4
      unimport5.6.0, 5.7.0, 6.2.0 (latest)
      h31.15.6

      The bug is consistent across all combinations above.

      Stack trace

      ReferenceError: H3Error is not defined
      at /path/to/.nuxt/dev/index.mjs:7673:12
      at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
      at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
      at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
      at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
      

      The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

      Diagnosis

      Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

      const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

      But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

      import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

      So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

      @nuxt/nitro-server registers these as auto-imports via:

      // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
      presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

      This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

      What we tried (none of these worked)

      1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
      2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
      3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
      4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
      5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
      6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

      Workaround that worked

      1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
      2. Hand-roll a Nitro middleware using @clerk/backend directly:
      // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
      secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

      This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

      With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

      Hypothesis

      Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

      I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

      Environment

      • macOS 26 (Apple Silicon)
      • Node.js (latest LTS)
      • yarn 1.22.22
      • Nuxt 4.4.4 with Vite/Nitro defaults
      • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

      Impact

      For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

      A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

      Environment

      - macOS 26 (Apple Silicon)
      - Node.js (latest LTS)
      - yarn 1.22.22
      - Nuxt 4.4.4 with Vite/Nitro defaults
      - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

      Metadata

      Metadata

      Assignees

      Labels

      needs-triageA ticket that needs to be triaged by a team member

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
        Skip to content

        [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

        Description

        @ericcorriel

        Preliminary Checks

        Reproduction

        https://github.com/ericcorriel/clerk-nuxt-bug

        Publishable key

        pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

        Description

        Repro repo

        https://github.com/ericorriel/clerk-nuxt-bug

        Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

        The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

        Summary

        When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

        Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

        Reproduced on

        PackageVersions tested
        nuxt4.3.1, 4.4.4 (latest stable)
        @clerk/nuxt2.2.8, 2.2.9 (latest stable)
        @nuxt/nitro-server4.3.1, 4.4.4
        unimport5.6.0, 5.7.0, 6.2.0 (latest)
        h31.15.6

        The bug is consistent across all combinations above.

        Stack trace

        ReferenceError: H3Error is not defined
        at /path/to/.nuxt/dev/index.mjs:7673:12
        at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
        at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
        at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
        at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
        

        The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

        Diagnosis

        Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

        const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

        But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

        import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

        So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

        @nuxt/nitro-server registers these as auto-imports via:

        // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
        presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

        This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

        What we tried (none of these worked)

        1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
        2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
        3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
        4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
        5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
        6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

        Workaround that worked

        1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
        2. Hand-roll a Nitro middleware using @clerk/backend directly:
        // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
        secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

        This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

        With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

        Hypothesis

        Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

        I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

        Environment

        • macOS 26 (Apple Silicon)
        • Node.js (latest LTS)
        • yarn 1.22.22
        • Nuxt 4.4.4 with Vite/Nitro defaults
        • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

        Impact

        For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

        A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

        Environment

        - macOS 26 (Apple Silicon)
        - Node.js (latest LTS)
        - yarn 1.22.22
        - Nuxt 4.4.4 with Vite/Nitro defaults
        - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

        Metadata

        Metadata

        Assignees

        Labels

        needs-triageA ticket that needs to be triaged by a team member

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
          Skip to content

          [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

          Description

          @ericcorriel

          Preliminary Checks

          Reproduction

          https://github.com/ericcorriel/clerk-nuxt-bug

          Publishable key

          pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

          Description

          Repro repo

          https://github.com/ericorriel/clerk-nuxt-bug

          Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

          The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

          Summary

          When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

          Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

          Reproduced on

          PackageVersions tested
          nuxt4.3.1, 4.4.4 (latest stable)
          @clerk/nuxt2.2.8, 2.2.9 (latest stable)
          @nuxt/nitro-server4.3.1, 4.4.4
          unimport5.6.0, 5.7.0, 6.2.0 (latest)
          h31.15.6

          The bug is consistent across all combinations above.

          Stack trace

          ReferenceError: H3Error is not defined
          at /path/to/.nuxt/dev/index.mjs:7673:12
          at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
          at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
          at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
          at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
          

          The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

          Diagnosis

          Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

          const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

          But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

          import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

          So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

          @nuxt/nitro-server registers these as auto-imports via:

          // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
          presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

          This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

          What we tried (none of these worked)

          1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
          2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
          3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
          4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
          5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
          6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

          Workaround that worked

          1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
          2. Hand-roll a Nitro middleware using @clerk/backend directly:
          // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
          secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

          This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

          With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

          Hypothesis

          Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

          I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

          Environment

          • macOS 26 (Apple Silicon)
          • Node.js (latest LTS)
          • yarn 1.22.22
          • Nuxt 4.4.4 with Vite/Nitro defaults
          • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

          Impact

          For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

          A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

          Environment

          - macOS 26 (Apple Silicon)
          - Node.js (latest LTS)
          - yarn 1.22.22
          - Nuxt 4.4.4 with Vite/Nitro defaults
          - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

          Metadata

          Metadata

          Assignees

          Labels

          needs-triageA ticket that needs to be triaged by a team member

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
            Skip to content

            [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

            Description

            @ericcorriel

            Preliminary Checks

            Reproduction

            https://github.com/ericcorriel/clerk-nuxt-bug

            Publishable key

            pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

            Description

            Repro repo

            https://github.com/ericorriel/clerk-nuxt-bug

            Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

            The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

            Summary

            When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

            Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

            Reproduced on

            PackageVersions tested
            nuxt4.3.1, 4.4.4 (latest stable)
            @clerk/nuxt2.2.8, 2.2.9 (latest stable)
            @nuxt/nitro-server4.3.1, 4.4.4
            unimport5.6.0, 5.7.0, 6.2.0 (latest)
            h31.15.6

            The bug is consistent across all combinations above.

            Stack trace

            ReferenceError: H3Error is not defined
            at /path/to/.nuxt/dev/index.mjs:7673:12
            at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
            at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
            at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
            at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
            

            The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

            Diagnosis

            Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

            const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

            But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

            import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

            So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

            @nuxt/nitro-server registers these as auto-imports via:

            // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
            presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

            This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

            What we tried (none of these worked)

            1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
            2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
            3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
            4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
            5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
            6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

            Workaround that worked

            1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
            2. Hand-roll a Nitro middleware using @clerk/backend directly:
            // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
            secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

            This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

            With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

            Hypothesis

            Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

            I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

            Environment

            • macOS 26 (Apple Silicon)
            • Node.js (latest LTS)
            • yarn 1.22.22
            • Nuxt 4.4.4 with Vite/Nitro defaults
            • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

            Impact

            For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

            A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

            Environment

            - macOS 26 (Apple Silicon)
            - Node.js (latest LTS)
            - yarn 1.22.22
            - Nuxt 4.4.4 with Vite/Nitro defaults
            - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

            Metadata

            Metadata

            Assignees

            Labels

            needs-triageA ticket that needs to be triaged by a team member

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
              Skip to content

              [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

              Description

              @ericcorriel

              Preliminary Checks

              Reproduction

              https://github.com/ericcorriel/clerk-nuxt-bug

              Publishable key

              pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

              Description

              Repro repo

              https://github.com/ericorriel/clerk-nuxt-bug

              Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

              The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

              Summary

              When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

              Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

              Reproduced on

              PackageVersions tested
              nuxt4.3.1, 4.4.4 (latest stable)
              @clerk/nuxt2.2.8, 2.2.9 (latest stable)
              @nuxt/nitro-server4.3.1, 4.4.4
              unimport5.6.0, 5.7.0, 6.2.0 (latest)
              h31.15.6

              The bug is consistent across all combinations above.

              Stack trace

              ReferenceError: H3Error is not defined
              at /path/to/.nuxt/dev/index.mjs:7673:12
              at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
              at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
              at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
              at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
              

              The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

              Diagnosis

              Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

              const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

              But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

              import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

              So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

              @nuxt/nitro-server registers these as auto-imports via:

              // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
              presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

              This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

              What we tried (none of these worked)

              1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
              2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
              3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
              4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
              5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
              6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

              Workaround that worked

              1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
              2. Hand-roll a Nitro middleware using @clerk/backend directly:
              // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
              secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

              This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

              With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

              Hypothesis

              Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

              I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

              Environment

              • macOS 26 (Apple Silicon)
              • Node.js (latest LTS)
              • yarn 1.22.22
              • Nuxt 4.4.4 with Vite/Nitro defaults
              • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

              Impact

              For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

              A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

              Environment

              - macOS 26 (Apple Silicon)
              - Node.js (latest LTS)
              - yarn 1.22.22
              - Nuxt 4.4.4 with Vite/Nitro defaults
              - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

              Metadata

              Metadata

              Assignees

              Labels

              needs-triageA ticket that needs to be triaged by a team member

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined · Issue #8430 · clerk/javascript · GitHub
                Skip to content

                [clerk-nuxt] Auto-registered Nitro server middleware breaks h3 auto-imports — ReferenceError: H3Error is not defined #8430

                Description

                @ericcorriel

                Preliminary Checks

                Reproduction

                https://github.com/ericcorriel/clerk-nuxt-bug

                Publishable key

                pk_test_aW5maW5pdGUtZmxlYS04Ni5jbGVyay5hY2NvdW50cy5kZXYk

                Description

                Repro repo

                https://github.com/ericorriel/clerk-nuxt-bug

                Standalone minimal Nuxt 4.4.4 + @clerk/nuxt 2.2.9 project. Clone, yarn install, copy .env.example to .env and paste Clerk dev keys, yarn dev, visit http://localhost:3000/ — the bug fires immediately on first request.

                The repo also includes the workaround we shipped (commented-out config block + a hand-rolled middleware in server/middleware/clerk.ts). Uncomment the workaround block in nuxt.config.ts, rm -rf .nuxt, restart, and the page renders cleanly.

                Summary

                When @clerk/nuxt is added to a Nuxt 4 app's modules array with default settings, every SSR-rendered page throws ReferenceError: H3Error is not defined at module-load time. The Nitro dev bundle's auto-imports virtual module references h3 helpers (H3Error, H3Event, appendCorsHeaders, and others) without emitting the corresponding import statements at the top of the bundle.

                Disabling Clerk's auto-registered server middleware via clerk: { skipServerMiddleware: true } is a workaround, but importing clerkMiddleware from @clerk/nuxt/server to register the middleware manually re-triggers the bug. Only avoiding @clerk/nuxt/server entirely (and using @clerk/backend directly) sidesteps it.

                Reproduced on

                PackageVersions tested
                nuxt4.3.1, 4.4.4 (latest stable)
                @clerk/nuxt2.2.8, 2.2.9 (latest stable)
                @nuxt/nitro-server4.3.1, 4.4.4
                unimport5.6.0, 5.7.0, 6.2.0 (latest)
                h31.15.6

                The bug is consistent across all combinations above.

                Stack trace

                ReferenceError: H3Error is not defined
                at /path/to/.nuxt/dev/index.mjs:7673:12
                at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
                at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
                at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
                at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
                

                The line number varies slightly with bundle size; the symbol may also be H3Event, appendCorsHeaders, setHeaders, etc. — anything in @nuxt/nitro-server's h3 auto-imports preset that user code doesn't already import explicitly.

                Diagnosis

                Inspecting the generated .nuxt/dev/index.mjs, the bundle contains an auto-imports virtual module:

                const_virtual__imports=/*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({__proto__: null,// ... user-defined symbols ...H3Error: H3Error,// <-- ReferenceError fires hereH3Event: H3Event,appendCorsHeaders: appendCorsHeaders,// ... more h3 symbols ...}));

                But the top-of-file h3 import only includes the subset of h3 helpers that user code references explicitly:

                import{defineEventHandler,createError,eventHandler,/* ... ~30 more ... */}from'h3';// H3Error, H3Event, appendCorsHeaders NOT in this list

                So H3Error/H3Event/etc. are referenced as values in the virtual imports object but were never imported into the bundle's scope.

                @nuxt/nitro-server registers these as auto-imports via:

                // node_modules/@nuxt/nitro-server/dist/index.mjs (at line 229 in 4.4.4)
                presets: [{from: 'h3',imports: ['H3Event','H3Error']}, ...]

                This preset works correctly when @clerk/nuxt is not in the modules array — the bundler emits the corresponding import statements and everything is fine. Adding @clerk/nuxt is the trigger.

                What we tried (none of these worked)

                1. Pinning unimport to 5.6.0, 5.7.0, and ^6.2.0 via yarn resolutions. Same error every time.
                2. Explicit user import: adding import { H3Error, H3Event } from 'h3' to a server plugin file. The bundler tree-shakes the plugin away or unimport strips the import as already-auto-registered. Either way the import statement never appears in the final bundle.
                3. Explicit nitro.imports.imports config with [{ name: 'H3Error', from: 'h3' }, ...]. No effect — the bundle still misses the import.
                4. Stripping the h3 value-preset via a nitro:config hook in nuxt.config.ts. This made H3Error materialize correctly, but the next preset entry (appendCorsHeaders) failed identically — the bug is preset-wide, not specific to particular symbols.
                5. Upgrading Nuxt from 4.3.1 to 4.4.4. Bug persists.
                6. Manual middleware registration in server/middleware/clerk.ts with import { clerkMiddleware } from '@clerk/nuxt/server'. This re-triggers the bug — importing anything from @clerk/nuxt/server is enough; the auto-registration isn't the only path that breaks the bundle.

                Workaround that worked

                1. Set clerk: { skipServerMiddleware: true } in nuxt.config.ts.
                2. Hand-roll a Nitro middleware using @clerk/backend directly:
                // server/middleware/clerk.tsimport{createClerkClient}from'@clerk/backend'letclerkSingleton: ReturnType<typeofcreateClerkClient>|null=nullexportdefaultdefineEventHandler(async(event)=>{constconfig=useRuntimeConfig(event)constsecretKey=config.clerk?.secretKeyif(!secretKey){event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})return}if(!clerkSingleton){clerkSingleton=createClerkClient({
                secretKey,publishableKey: config.public?.clerk?.publishableKey})}try{constrequestState=awaitclerkSingleton.authenticateRequest(toWebRequest(event),{acceptsToken: 'any'})event.context.auth=()=>requestState.toAuth()if(requestState.headers){requestState.headers.forEach((value,key)=>setResponseHeader(event,key,value))}}catch{event.context.auth=()=>({isAuthenticated: false,userId: null,sessionId: null,sessionClaims: null})}})

                This is functionally identical to the clerkMiddleware() export from @clerk/nuxt/server for our use case (we don't use keyless mode or the handshake redirect flow). Importantly, it imports only from @clerk/backend (a transitive dep we already pull in via @clerk/nuxt) and from h3 — no @clerk/nuxt/server path is involved at the bundle level.

                With this in place, the bundler emits import { ... } from 'h3' correctly with all the symbols the auto-imports virtual module references, the bundle loads, and event.context.auth() works exactly as before.

                Hypothesis

                Something in @clerk/nuxt's server-runtime entry or its internal exports interacts with unimport's scanner in a way that registers h3 auto-imports as "tracked" but somehow short-circuits the bundler's import-injection step. The corruption manifests in any code path that pulls @clerk/nuxt/server into the bundle — auto-registered middleware, manually-registered middleware via clerkMiddleware() import, possibly other entry points.

                I haven't isolated the exact mechanism, but the workaround above sidesteps it cleanly by using @clerk/backend directly. Happy to add more diagnostics if helpful.

                Environment

                • macOS 26 (Apple Silicon)
                • Node.js (latest LTS)
                • yarn 1.22.22
                • Nuxt 4.4.4 with Vite/Nitro defaults
                • Other Nuxt modules in our production project: shadcn-nuxt, @nuxtjs/tailwindcss, @nuxtjs/color-mode. Removing these does not affect the bug — the standalone repro repo above includes none of them.

                Impact

                For any Nuxt 4 app adopting @clerk/nuxt server-side, this is a hard blocker on default usage — every SSR page crashes. The workaround restores functionality but adds maintenance burden (the project has to track when the upstream fix lands and remove the workaround). It also subtly changes behavior on edge cases not covered by the hand-rolled middleware (keyless mode, handshake redirects).

                A Nuxt-4 + Clerk migration was the trigger that surfaced this for us; we're shipping the workaround in production while we wait for an upstream fix. Happy to test patches or canaries against the repro repo.

                Environment

                - macOS 26 (Apple Silicon)
                - Node.js (latest LTS)
                - yarn 1.22.22
                - Nuxt 4.4.4 with Vite/Nitro defaults
                - Other Nuxt modules in our production project: `shadcn-nuxt`, `@nuxtjs/tailwindcss`, `@nuxtjs/color-mode`. Removing these does not affect the bug — the standalone repro repo above includes none of them.

                Metadata

                Metadata

                Assignees

                Labels

                needs-triageA ticket that needs to be triaged by a team member

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions