Skip to content

[🐞] v2: intermittent build failure with Deno if useAsync$ (or alike) is used #8546

@NaitLee

Description

@NaitLee

Which component is affected?

Qwik Rollup / Vite plugin

Describe the bug

When using “async tasks” in qwik v2, deno task build fails “intermittently.”

“Async tasks” include:

  • useAsync$(...)
  • useTask$(async () => { await ... })
  • useTask$(() => { return new Promise(...) })

The build either succeeds, or fails with this error message from Rollup, randomly:

error during build:
Unexpected early exit. This happens when Promises returned by plugins cannot resolve. Unfinished hook action(s) on exit:
(vite-plugin-qwik-router-server-fns) load "\u0000virtual:qwik-router-server-fns"
    at Process.handleBeforeExit (file:///tmp/testrace/node_modules/.deno/rollup@4.60.1/node_modules/rollup/dist/es/shared/node-entry.js:23313:28)
    at Object.onceWrapper (ext:deno_node/_events.mjs:564:12)
    at Process.emit (ext:deno_node/_events.mjs:439:20)
    at Process.emit (node:process:432:38)
    at dispatchProcessBeforeExitEvent (node:process:879:13)

The problem is Deno-only. Building with Node.js and Bun proceed normally.

Reproduction

https://stackblitz.com/edit/github-5balyysx?file=src%2Froutes%2Findex.tsx

Steps to reproduce

Create a qwik v2 app:

cd /tmp
deno run -A npm:create-qwik@2.0.0-beta.31 empty testrace -i
cd testrace

Write minimal useAsync$ code:

// src/routes/index.tsx
import { component$, useAsync$ } from "@qwik.dev/core";

export default component$(() => {
  const fruit = useAsync$<string>(() =>
    new Promise((resolve) => resolve("apple"))
  );
  return <div>Fruit: {fruit.value}</div>;
});

Add ssg adapter and build with Deno:

deno task qwik add ssg
deno task build

Sometimes (not everytime) Rollup fails with this:

error during build:
Unexpected early exit. This happens when Promises returned by plugins cannot resolve. Unfinished hook action(s) on exit:
(vite-plugin-qwik-router-server-fns) load "\u0000virtual:qwik-router-server-fns"
    at Process.handleBeforeExit (file:///tmp/testrace/node_modules/.deno/rollup@4.60.1/node_modules/rollup/dist/es/shared/node-entry.js:23313:28)
    at Object.onceWrapper (ext:deno_node/_events.mjs:564:12)
    at Process.emit (ext:deno_node/_events.mjs:439:20)
    at Process.emit (node:process:432:38)
    at dispatchProcessBeforeExitEvent (node:process:879:13)
Full build log
$ deno task build
Task build qwik build

      ............
    .::: :--------:.
   .::::  .:-------:.
  .:::::.   .:-------.
  ::::::.     .:------.
 ::::::.        :-----:
 ::::::.       .:-----.
  :::::::.     .-----.
   ::::::::..   ---:.
    .:::::::::. :-:.
     ..::::::::::::
             ...::::
     


deno run build.types
deno run build.client
deno run build.server
deno run lint

Task build.types tsc --incremental --noEmit "--pretty"
Task build.client vite build
vite v7.3.1 building client environment for production...
✓ 60 modules transformed.
dist/assets/Dzh8xAVZ-bundle-graph.json   1.07 kB │ gzip:  0.66 kB
dist/q-manifest.json                    30.25 kB │ gzip:  4.22 kB
dist/build/q-Byp9lfIg.js                 0.11 kB │ gzip:  0.12 kB
dist/build/q-CPEBto7H.js                 0.13 kB │ gzip:  0.13 kB
dist/build/q-Czi1LwSX.js                 0.15 kB │ gzip:  0.14 kB
dist/build/q-DBgbZXTS.js                 0.18 kB │ gzip:  0.17 kB
dist/build/q-Ai7h09MO.js                 0.19 kB │ gzip:  0.17 kB
dist/build/q-DWQ3sDuw.js                 0.20 kB │ gzip:  0.19 kB
dist/build/q-5XCLoW3i.js                 0.24 kB │ gzip:  0.20 kB
dist/build/q-Cc9Qra6O.js                 0.31 kB │ gzip:  0.25 kB
dist/build/q-BntSa9pJ.js                 0.39 kB │ gzip:  0.25 kB
dist/build/q-fmG5G1zC.js                 0.53 kB │ gzip:  0.33 kB
dist/build/q-tNFKMFKY.js                 0.53 kB │ gzip:  0.36 kB
dist/build/q-CaDOpu5t.js                 0.55 kB │ gzip:  0.34 kB
dist/build/q-CysUL0ow.js                 0.63 kB │ gzip:  0.34 kB
dist/build/q-BKT_gTbX.js                 0.80 kB │ gzip:  0.47 kB
dist/build/q-CcNa2poI.js                 0.89 kB │ gzip:  0.54 kB
dist/build/q-ogTKInb-.js                 0.90 kB │ gzip:  0.52 kB
dist/build/q-RPUTNPji.js                 0.90 kB │ gzip:  0.55 kB
dist/build/q-BvmD9iPO.js                 1.36 kB │ gzip:  0.76 kB
dist/build/q-xmCVacXr.js                 1.55 kB │ gzip:  0.84 kB
dist/build/q-0jgfnq-U.js                 1.99 kB │ gzip:  0.98 kB
dist/build/q-B9id7zUI.js                 2.62 kB │ gzip:  1.02 kB
dist/build/q-7wzWdMr9.js                 3.66 kB │ gzip:  1.87 kB
dist/build/q-VPloE5mA.js                 4.76 kB │ gzip:  2.42 kB
dist/build/q-B81hBnHe.js                 7.20 kB │ gzip:  2.92 kB
dist/build/q-AZviV0SD.js                11.15 kB │ gzip:  4.82 kB
dist/build/q-Amb1vhsw.js                97.84 kB │ gzip: 37.54 kB
✓ built in 923ms

✓ Built client modules
Task build.server qwik check-client src dist && vite build -c adapters/ssg/vite.config.ts
Task lint eslint "src/**/*.ts*"

      ............
    .::: :--------:.
   .::::  .:-------:.
  .:::::.   .:-------.
  ::::::.     .:------.
 ::::::.        :-----:
 ::::::.       .:-----.
  :::::::.     .-----.
   ::::::::..   ---:.
    .:::::::::. :-:.
     ..::::::::::::
             ...::::
     

vite v7.3.1 building ssr environment for production...
transforming (33) node_modules/.deno/@qwik.dev+router@2.0.0-beta.31/node_modules/@qwik.dev/router/lib/chunks/error-handler.mjsRead client manifest from /tmp/testrace/dist/q-manifest.json
✗ Build failed in 414ms
error during build:
Unexpected early exit. This happens when Promises returned by plugins cannot resolve. Unfinished hook action(s) on exit:
(vite-plugin-qwik-router-server-fns) load "\u0000virtual:qwik-router-server-fns"
    at Process.handleBeforeExit (file:///tmp/testrace/node_modules/.deno/rollup@4.60.1/node_modules/rollup/dist/es/shared/node-entry.js:23313:28)
    at Object.onceWrapper (ext:deno_node/_events.mjs:564:12)
    at Process.emit (ext:deno_node/_events.mjs:439:20)
    at Process.emit (node:process:432:38)
    at dispatchProcessBeforeExitEvent (node:process:879:13)

undefined

ExecaError: Command failed with exit code 1: deno run build.server

useTask$ with promises has same problem too.

import { component$, useSignal, useTask$ } from "@qwik.dev/core";

export default component$(() => {
  const fruit = useSignal("");
  useTask$(async () => {
    fruit.value = await new Promise((resolve) => resolve("apple"));
  });
  return <div>Fruit: {fruit.value}</div>;
});

System Info

System:
    OS: Linux 6.19 Debian GNU/Linux forky/sid
    CPU: (16) x64 AMD Eng Sample: 100-000000300-40_Y
    Memory: 17.32 GB / 30.75 GB
    Container: Yes
    Shell: 5.3.9 - /usr/bin/bash
  Binaries:
    Node: 25.6.1 - /opt/app/node/current/bin/node
    npm: 11.9.0 - /opt/app/node/current/bin/npm
    bun: 1.3.11 - /opt/app/bun-linux-x64/bun
    Deno: 2.7.12 - /opt/app/deno
  Browsers:
    Chromium: 146.0.7680.177
    Firefox: 140.9.0esr
    Firefox Developer Edition: 140.9.0esr

* My system isn’t a container, but a minimal Debian system installed with `debootstrap`. It shouldn’t matter anyway ;)

Additional Information

The problem is very possibly upstream (of Deno or Vite/Rollup), but given their complexity, I don’t have time to verify it.

I suspect these code in Rollup around the given node_modules/rollup/dist/es/shared/node-entry.js:23313:28:

async function catchUnfinishedHookActions(pluginDriver, callback) {
    const emptyEventLoopPromise = new Promise((_, reject) => {
        // ...
                    reject(new Error(`Unexpected early exit. This happens when Promises returned by plugins cannot resolve. ...
        // ...
    });
    try {
        return await Promise.race([callback(), emptyEventLoopPromise]);
    }
    // ...

Probably Promise.race (or the event loop) behavior differs in Deno? I’m wondering if we can have migrations in qwik codebase.

It’s unknown if the problem will persist after the Rolldown migration (#8379).

The reproduction url contains code, but the environment only has node.js 18. If possible, verify locally with Deno, try running deno task build may times (like more than 5).

EDIT: useTask$(() => { new Promise(...); return void 0; }); is anti-pattern, should always return the promise(s).

Metadata

Metadata

Assignees

Labels

V2bugSomething isn't workingrouter

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions