No stack trace for errors

Hi,
I have created a simple project to help recreate the setup (this post is based on 4f4ba25). It’s webpack 5 (using custom builds) and TypeScript (using ts-loader).

Intro

I’m unsure what a correct setup would look like for this as neither the docs nor the blog posts seem to actually show debugging a Worker, only some of the tools. I’m thinking of specifically console errors, not extra logging libraries and tools like Sentry. In short, I can’t seem to get the full stack trace of unhandled errors, but console.error does provide a stack trace (only when using wrangler preview, not wrangler dev).

Source
const url = new URL(request.url);
const searchParams = url.searchParams;
const throwType = searchParams.get("type");
switch (throwType) {
  case null: {
    return new Response("[a30b7b07] No error");
  }
  case "string": {
    throw "[be3bd631] Thrown string";
  }
  case "error": {
    throw new Error("[735ff7c3] Thrown Error");
  }
  case "fetch": {
    await fetch("[626b61d9] Failed fetch");
    return new Response("[b0978a42] Failed fetch response");
  }
  case "promise-catch": {
    (async () => {
      throw "[294e277c] Try thrown string";
    })().catch((err) => {
      console.error("[380fa5f8]", err);

      // Based on initial contents of new Worker created from dashboard
      console.error("[7fe953d6]", err.stack);
    });
    return new Response("[b799fa3f] Promise catch response");
  }
  default: {
    return new Response("[be4e1f66] Not found");
  }
}

wrangler dev

When running wrangler dev and going through each of the throw types in order, I get the following in my terminal (responses added separately):

Logs
... GET cloudflare-workers-test-dev.<user>.workers.dev/throw HTTP/1.1 200 OK
[Response: a30b7b07]

... GET cloudflare-workers-test-dev.<user>.workers.dev/throw?type=string HTTP/1.1 500 Internal Server Error
Uncaught (in promise)
Uncaught (in response)
Error: internal error
[Response: 1101]

Uncaught (in promise)
Error: [735ff7c3] Thrown Error
    at worker.js:1:318
    at e (worker.js:1:723)
    at worker.js:1:64
Uncaught (in response)
Error: [735ff7c3] Thrown Error
... GET cloudflare-workers-test-dev.<user>.workers.dev/throw?type=error HTTP/1.1 500 Internal Server Error
[Response: 1101]


Uncaught (in promise)
TypeError: Fetch API cannot load: [626b61d9] Failed fetch
Uncaught (in response)
TypeError: Fetch API cannot load: [626b61d9] Failed fetch
... GET cloudflare-workers-test-dev.<user>.workers.dev/throw?type=fetch HTTP/1.1 500 Internal Server Error
[Response: 1101]

... GET cloudflare-workers-test-dev.<user>.workers.dev/throw?type=promise-catch HTTP/1.1 200 OK
[380fa5f8] [294e277c] Try thrown string
[7fe953d6] undefined
[Response: b799fa3f]

As you can see, none of the errors seem to have much information, just the error’s main string representation. This means that if there is an error in a larger project, finding where the error is actually from isn’t really possible. Based on some of the resources, wrangler dev is meant to help debugging, which suggests this is not normal, but I can’t seem to see what is causing the lack of error information.

The throw new Error (type=error) does include a stack trace, but it is not using the inline source map.

wrangler preview

I also tried wrangler preview:

wrangler preview console

image


image


image


image

Source (same as GitHub repo linked above at commit 4f4ba25):

Source

image


image

There also seems to be a lack of context shown in wrangler preview, with the only context coming from console.error, not any of the unhandled errors. All unhandled errors seem to reference the line with event.respondWith() and don’t include further details.

Here, throw Error behaves differently again, with it linking to the original worker.js despite it including an inline source map.

What I tried

To try to get more context, after seeing that throw Error worked with wrangler dev, I tried:

addEventListener("fetch", (event: FetchEvent) => {
  event.respondWith(
    (async () => {
      try {
        return await handleRequest(event.request);
      } catch (err) {
        throw err;
      }
    })()
  );
});

This didn’t help, for example:

... GET cloudflare-workers-test-dev.<user>.workers.dev/throw?type=string HTTP/1.1 500 Internal Server Error
Uncaught (in promise)
Uncaught (in response)
Error: internal error

I also tried using the template given when creating a new Worker from the Cloudflare Dashboard:

addEventListener("fetch", (event: FetchEvent) => {
  event.respondWith(handleRequest(event.request).catch((err) => new Response(err.stack, { status: 500 })));
});

When using wrangler dev and type=string, I got a response with no content.

Question

Is there a way to have the expected context for all of these cases in both wrangler dev and wrangler preview? This would include references to the original source (using the source map) and a full stack trace instead of referencing event.respondWith(handleRequest(event.request)).

2 Likes

I’m encountering a similar problem where some errors thrown by Cloudflare Workers internals don’t include a stack trace, e.g. when response.json() throws

Here’s an example worker to repro:

async function captureAndRethrow(e) {
  if (!e.stack.includes('\n')) {
    Error.captureStackTrace(e)
  }
  throw e
}

async function foo() {
  const res = await fetch('foo://bar')
  const data = await res.json().catch(captureAndRethrow)
  return data
}


export default {
  async fetch(request, env, context) {
    try {
      const res = await foo()
      return new Response(res)
    } catch (err) {
      return new Response(err.stack)
    }
  }
}

Without the .catch(captureAndRethrow) part, err.stack will be just:

SyntaxError: Unexpected token < in JSON at position 1

But when adding captureAndRethrow, we get a more useful stack trace:

SyntaxError: Unexpected token < in JSON at position 1
    at captureAndRethrow (worker.js:2:9)
    at async foo (worker.js:8:16)
    at async Object.fetch (worker.js:16:19)

Until this get fixed upstream I’m just adding .catch(captureAndRethrow) everywhere I need visibility