Skip to content

QRL chunks fail to load on cold Vite dev SSR requests (beta.35 + Vite 8) — TypeError: Importing a module script failed + 504 Outdated Optimize Dep #8634

@lapinponpin

Description

@lapinponpin

Which area(s) are affected?

Qwik Runtime, Rollup / Vite plugin

Describe the bug

In Vite dev SSR (pnpm dev), the first request to a route that uses useVisibleTask$ produces TypeError: Importing a module script failed on multiple QRL chunks. Subsequent requests succeed.

qrl WebVitals_component_useVisibleTask_yn4FTDO7PRs failed to load TypeError: Importing a module script failed.
qrl Link_component_useVisibleTask_xKeuRmnoNSA failed to load TypeError: Importing a module script failed.
qrl PWAProvider_component_useVisibleTask_PiqqFnaN19Y failed to load TypeError: Importing a module script failed.

Often accompanied by a 504 (Outdated Optimize Dep) from Vite for one of the requested chunks:

Failed to load resource: the server responded with a status of 504 (Outdated Optimize Dep)

The pattern looks like Vite's dep-optimization invalidating chunk URLs mid-request: the page renders, qwikloader starts importing QRL chunks, Vite re-optimizes and changes the chunk hashes, the in-flight imports point at stale URLs and 504.

History across versions:

  • beta.32: Present but invisible — qwikloader handled the failure gracefully.
  • beta.34: Became deterministic, broke pages outright. We held at beta.32 for two release cycles.
  • beta.35: PR fix(preloader): don't include preloader in dev mode #8606 (skip preloader in dev) reduced it to a one-retry flake under Playwright. Shipped, but still visible on cold loads.

Production builds are unaffected — only Vite dev SSR.

Reproduction

I don't have a self-contained min-repro yet. The flake appears tied to overall project complexity / dependency-graph size and may not surface in a trimmed-down project. Filing this with embedded evidence and willing to construct a min-repro if a maintainer can suggest what scale of components / cold-load conditions to target.

What our setup looks like (private codebase, so I can't link it):

  • ~6 components using useVisibleTask$ near the entry route (/auth/sign-in)
  • Cloudflare-Workers SSR adapter, Vite dev for local
  • Playwright cold-loads in WebKit (mobile-webkit project) and Chromium

Symptom timing on cold load:

  1. First request: 15–17 s wall time, Importing a module script failed on 2–4 QRL chunks, page errors in console (functionality not necessarily broken, but tests checking errors.toEqual([]) fail).
  2. Retry: 1.5–3.5 s, succeeds cleanly.

If we re-enable Qwik's built-in qwikLoader: "module" (default), qwikloader itself joins the failed-chunk set and interactivity breaks deterministically on Chromium (market-explorer: navigate to detail, portfolio-grid: search filters failed even after 2 retries).

Steps to reproduce

Approximate — minimum complexity to surface this isn't established yet:

  1. Qwik app with multiple useVisibleTask$ components active on entry route.
  2. pnpm dev (Vite dev SSR, not built).
  3. Cold-load entry route in Playwright WebKit, capture console.error.
  4. Expect: empty error list. Observed: 2–4 Importing a module script failed errors.
  5. Reload: clean.

System Info

System:
  OS: macOS 26.5 (Apple Silicon, Apple M5 Pro)
  Shell: zsh 5.9
Binaries:
  Node: 22.22.2
  pnpm: 10.33.2
Browsers (also confirmed in Playwright Linux Ubuntu CI):
  Safari: 26.5
  Chrome: 148.0.7778.97
npmPackages:
  @qwik.dev/core: 2.0.0-beta.35
  @qwik.dev/router: 2.0.0-beta.35
  @playwright/test: 1.60.0
  vite: 8.0.13

CI environment where the same pattern reproduces: Ubuntu 24.04, Node 22, fresh pnpm dev, Playwright mobile-webkit against http://localhost:3000.

Additional Information

Workaround that's load-bearing for us today — removing it deterministically breaks interactivity:

// entry.ssr.tsx
return renderToStream(<Root />, {
  ...opts,
  qwikLoader: "never",       // skip Qwik's built-in qwikloader emission
  manifest,
});
// root.tsx
<head>
  {/* ... */}
  <script src="/qwikloader.js" async />    {/* non-module static asset */}
</head>

/qwikloader.js is a hand-copy of node_modules/@qwik.dev/core/dist/qwikloader.js wrapped in an IIFE, served as a non-module script. This bypasses Vite's dev module resolver entirely.

Hypothesis: Vite's dep-optimization cache for SSR is invalidating chunk URLs mid-import during cold requests, and Qwik's QRL loader doesn't tolerate the 504 Outdated Optimize Dep response. The non-module static loader works because Vite doesn't intercept non-module <script> tags.

Happy to build a minimal repro, capture HAR + Vite dep-cache state during a cold request, or run targeted experiments (e.g., disable optimizeDeps, vary number of useVisibleTask$ components) if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions