Skip to content

Dev server hangs on first request with Cloudflare adapter: circular deadlock in ensureModulesLoaded() (regression from #15988) #16010

@mariusbolik

Description

@mariusbolik

Astro Info

Astro                    v6.0.7
Node                     v22.x
System                   macOS (arm64)
Package Manager          bun
Output                   server
Adapter                  @astrojs/cloudflare v13

Describe the Bug

After upgrading to 6.0.7, the dev server (astro dev with @astrojs/cloudflare) hangs permanently on the first HTTP request. The server starts successfully and prints the ready URL, but every request times out — no response is ever returned. The issue does not occur on 6.0.6.

This is a regression introduced by PR #15988, which added ensureModulesLoaded() to vite-plugin-css/index.js.

Root Cause

ensureModulesLoaded() is called from the load handler of virtual:astro:dev-css:X (a per-route CSS collector module). It walks the full SSR module graph starting from the corresponding virtual:astro:page:X module. The graph contains virtual:astro:dev-css-all because pipeline.js imports it with a static string literal:

const { devCSSMap } = await import("virtual:astro:dev-css-all");

Vite tracks static dynamic imports in importedModules, so virtual:astro:dev-css-all always appears in the graph. That module's importedModules in turn contains all per-route virtual:astro:dev-css:*.astro modules (because virtual:astro:dev-css-all statically imports them all via the generated map).

When ensureModulesLoaded() reaches one of those virtual:astro:dev-css:Y modules and finds it lacks a transformResult, it calls:

await env.fetchModule(imp.id);  // imp.id = "virtual:astro:dev-css:Y"

But virtual:astro:dev-css:Y's own load handler is already running (its ensureModulesLoaded call is in progress). Vite's _pendingRequests map causes the second fetchModule call to wait for the first to complete — which can never happen because both are waiting on each other. Permanent deadlock.

This is specific to the Cloudflare adapter because workerd requests modules concurrently via __VITE_INVOKE_MODULE__, making it likely that multiple virtual:astro:dev-css:* load handlers run simultaneously.

Fix

Inside ensureModulesLoaded(), skip virtual:astro:dev-css:* and virtual:astro:dev-css-all modules to break the cycle:

async function ensureModulesLoaded(env, mod, seen = new Set()) {
  const id = mod.id ?? mod.url;
  if (seen.has(id)) return;
  seen.add(id);
  for (const imp of mod.importedModules) {
    if (!imp.id) continue;
    if (seen.has(imp.id)) continue;
    if (imp.id.includes(PROPAGATED_ASSET_QUERY_PARAM)) continue;
    // Skip virtual dev-css modules to prevent circular deadlocks:
    // these collector modules reference each other via the dev-css-all map,
    // causing fetchModule calls on modules currently being transformed.
    if (
      imp.id.startsWith(RESOLVED_MODULE_DEV_CSS_PREFIX) ||
      imp.id === RESOLVED_MODULE_DEV_CSS ||
      imp.id === RESOLVED_MODULE_DEV_CSS_ALL
    ) continue;
    if (!imp.transformResult) {
      try {
        await env.fetchModule(imp.id);
      } catch {}
    }
    await ensureModulesLoaded(env, imp, seen);
  }
}

This is safe because the virtual dev-css modules are CSS collectors — they don't contain CSS themselves. Skipping them during the pre-warm traversal has no effect on CSS injection; collectCSSWithOrder() handles the actual CSS gathering separately.

What's the expected result?

Dev server responds to requests normally, as it did in 6.0.6.

Steps to Reproduce

  1. Use @astrojs/cloudflare v13 adapter with a moderately large project (multiple routes, third-party integrations that add SSR modules)
  2. Run astro dev
  3. Open any page — the request hangs indefinitely

Projects with small SSR module graphs may not hit this because ensureModulesLoaded needs to traverse deep enough to reach virtual:astro:dev-css-all before the deadlock triggers. Adding packages excluded from optimizeDeps (like @sentry/astro) increases graph size and makes the issue reliably reproducible.

Link to Minimal Reproducible Example

No minimal repro yet, but the fix above applied to node_modules/astro/dist/vite-plugin-css/index.js resolves the issue completely.

Participation

  • I am willing to submit a pull request for this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    - P5: urgentFix build-breaking bugs affecting most users, should be released ASAP (priority)auto triage skippedneeds triageIssue needs to be triaged

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions