Skip to content

feat(qwik-city): more info for unhandled exceptions#8655

Merged
wmertens merged 4 commits into
mainfrom
robust
May 21, 2026
Merged

feat(qwik-city): more info for unhandled exceptions#8655
wmertens merged 4 commits into
mainfrom
robust

Conversation

@wmertens
Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings May 21, 2026 12:04
@wmertens wmertens requested review from a team as code owners May 21, 2026 12:04
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 21, 2026

🦋 Changeset detected

Latest commit: bea82f0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@builder.io/qwik-city Patch
@builder.io/qwik Patch
eslint-plugin-qwik Patch
create-qwik Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces request-aware diagnostics for unhandled crashes in Qwik City integrations by exposing the “current request event” via a new public API, and then using it in starter templates to enrich uncaught-exception logging. It also hardens resource deserialization against thenable/promise-like values and improves how request-handler completion errors are surfaced for edge adapters.

Changes:

  • Add getRequestEvent() as a public Qwik City runtime export, with generated API/docs updates and unit tests.
  • Update multiple starter adapters (Node/Express/Fastify/Cloud Run/Bun/Deno) to log request metadata on unhandled exceptions (and in some runtimes, unhandled rejections).
  • Harden ResourceSerializer against deserialized thenables and ensure rejected resource promises are handled without triggering unhandled-rejection noise; add unit tests.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
starters/apps/perf.prod/src/entry.express.tsx Adds uncaught-exception monitoring and logs request context using getRequestEvent().
starters/adapters/node-server/src/entry.node-server.tsx Adds uncaught-exception monitoring and logs request context using getRequestEvent().
starters/adapters/fastify/src/entry.fastify.tsx Adds uncaught-exception monitoring and logs request context using getRequestEvent().
starters/adapters/express/src/entry.express.tsx Adds uncaught-exception monitoring and logs request context using getRequestEvent().
starters/adapters/deno/src/entry.deno.ts Adds global error/unhandledrejection listeners and logs request context via getRequestEvent().
starters/adapters/cloud-run/src/entry.cloud-run.tsx Adds uncaught-exception monitoring and logs request context using getRequestEvent().
starters/adapters/bun/src/entry.bun.ts Adds uncaught-exception/unhandled-rejection handlers and logs request context via getRequestEvent().
packages/qwik/src/core/container/serializers.ts Adds thenable detection + handled rejected promises; rejects unsafe deserialized resource values.
packages/qwik/src/core/container/serializers.unit.ts Adds unit tests for ResourceSerializer thenable-rejection behavior.
packages/qwik-city/src/runtime/src/server-functions.ts Exports new public getRequestEvent() helper.
packages/qwik-city/src/runtime/src/server-functions.unit.ts Adds unit tests covering getRequestEvent() behavior with/without async request store.
packages/qwik-city/src/runtime/src/index.ts Re-exports getRequestEvent() from the runtime entrypoint.
packages/qwik-city/src/runtime/src/qwik-city.runtime.api.md Updates API extractor output to include getRequestEvent.
packages/qwik-city/src/middleware/request-handler/user-response.ts Makes completion always resolve (captures errors) and handles createRequestEvent failures.
packages/qwik-city/src/middleware/request-handler/user-response.unit.ts Adds test for runQwikCity behavior when request-event creation fails.
packages/qwik-city/src/middleware/vercel-edge/index.ts Renames completion callback var to err and logs completion errors.
packages/qwik-city/src/middleware/netlify-edge/index.ts Renames completion callback var to err and logs completion errors.
packages/qwik-city/src/middleware/deno/index.ts Renames completion callback var to err and logs completion errors.
packages/qwik-city/src/middleware/cloudflare-pages/index.ts Renames completion callback var to err and logs completion errors.
packages/qwik-city/src/middleware/bun/index.ts Renames completion callback var to err and logs completion errors.
packages/docs/src/routes/api/qwik-city/index.mdx Adds API docs section entry for getRequestEvent.
packages/docs/src/routes/api/qwik-city/api.json Adds getRequestEvent entry to generated API JSON.
.changeset/swift-numbers-reply.md Adds changeset documenting the new getRequestEvent() feature.
Comments suppressed due to low confidence (5)

packages/qwik-city/src/middleware/vercel-edge/index.ts:101

  • In the createRequestEvent failure path, handledResponse.response can resolve to null while handledResponse.completion resolves to an error. In that case this middleware will currently fall through to the 404 response even though an internal error occurred. Consider: if response is falsy, await completion and return a 500 response when it contains an error (instead of returning Not Found).
      const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
      if (handledResponse) {
        handledResponse.completion.then((err) => {
          if (err) {
            console.error(err);
          }
        });
        const response = await handledResponse.response;
        if (response) {
          return response;
        }
      }

packages/qwik-city/src/middleware/netlify-edge/index.ts:74

  • Same as other edge adapters: if handledResponse.response resolves to null but handledResponse.completion resolves to an error (e.g., request event creation failure), this will fall back to the 404 path. Consider checking completion when response is falsy and returning a 500 instead of Not Found.
      // send request to qwik city request handler
      const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
      if (handledResponse) {
        handledResponse.completion.then((err) => {
          if (err) {
            console.error(err);
          }
        });
        const response = await handledResponse.response;
        if (response) {
          return response;
        }
      }

packages/qwik-city/src/middleware/deno/index.ts:99

  • If handledResponse.response resolves to null but handledResponse.completion resolves to an error (e.g., createRequestEvent throwing), this router returns null (treated as “not handled”), which can lead to a misleading 404 later. Consider awaiting completion when response is falsy and returning a 500 response when an error is present.
      // send request to qwik city request handler
      const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
      if (handledResponse) {
        handledResponse.completion.then((err) => {
          if (err) {
            console.error(err);
          }
        });
        const response = await handledResponse.response;
        if (response) {
          return response;
        }
      }

packages/qwik-city/src/middleware/cloudflare-pages/index.ts:113

  • If handledResponse.response resolves to null but handledResponse.completion resolves to an error (e.g., request event creation failure), this will currently fall through to the 404 response. Consider checking completion when response is falsy and returning a 500 response instead of Not Found so internal errors aren’t masked as 404s.
      // send request to qwik city request handler
      const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
      if (handledResponse) {
        handledResponse.completion.then((err) => {
          if (err) {
            console.error(err);
          }
        });
        const response = await handledResponse.response;
        if (response) {
          if (response.ok && cache && response.headers.has('Cache-Control')) {
            // Store the fetched response as cacheKey
            // Use waitUntil so you can return the response without blocking on
            // writing to cache
            ctx.waitUntil(cache.put(cacheKey, response.clone()));
          }
          return response;
        }
      }

packages/qwik-city/src/middleware/bun/index.ts:98

  • If handledResponse.response resolves to null but handledResponse.completion resolves to an error (e.g., createRequestEvent throwing), this router returns null and the request is treated as “not handled”. Consider awaiting completion when response is falsy and returning a 500 response when an error is present so failures don’t get misclassified as not-found.
      // send request to qwik city request handler
      const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
      if (handledResponse) {
        handledResponse.completion.then((err) => {
          if (err) {
            console.error(err);
          }
        });
        const response = await handledResponse.response;
        if (response) {
          // bun fails to redirect if there is a body.
          // remove the body if there a redirect.
          const status = response.status;
          const location = response.headers.get('Location');
          const isRedirect = status >= 301 && status <= 308 && location;
          if (isRedirect) {
            return new Response(null, response);
          }
          return response;
        }
      }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/qwik-city/src/runtime/src/server-functions.unit.ts
Comment thread packages/qwik-city/src/middleware/request-handler/user-response.ts
Comment thread packages/qwik/src/core/container/serializers.ts
Comment thread starters/adapters/express/src/entry.express.tsx
Comment thread starters/adapters/cloud-run/src/entry.cloud-run.tsx
Comment thread starters/apps/perf.prod/src/entry.express.tsx
Comment thread starters/adapters/deno/src/entry.deno.ts Outdated
Comment thread starters/adapters/deno/src/entry.deno.ts
Comment thread starters/adapters/bun/src/entry.bun.ts
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 21, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@builder.io/qwik@8655
npm i https://pkg.pr.new/@builder.io/qwik-city@8655
npm i https://pkg.pr.new/eslint-plugin-qwik@8655
npm i https://pkg.pr.new/create-qwik@8655

commit: bea82f0

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
qwik-docs ✅ Ready (View Log) Visit Preview bea82f0

Varixo
Varixo previously approved these changes May 21, 2026
Copy link
Copy Markdown
Member

@Varixo Varixo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but look at AI comments

@wmertens wmertens merged commit 64aa8b4 into main May 21, 2026
21 checks passed
@wmertens wmertens deleted the robust branch May 21, 2026 19:11
@github-project-automation github-project-automation Bot moved this from Waiting For Review to Done in Qwik Development May 21, 2026
@github-actions github-actions Bot mentioned this pull request May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants