Debugging Tips

Error pages generated by Workers

When a Worker running in production has an error that prevents it from returning a response, the client will receive an error page with an error code, defined as follows:

Error code Meaning
1101 Worker threw a JavaScript exception.
1102 Worker exceeded CPU time limit. See: Resource Limits
1015 Your client IP is being rate limited.


Other 11xx errors generally indicate a problem with the Workers runtime itself - please check our status page if you see one.

Debug tips

Ideally, you write and test your Worker in the playground using wrangler preview or directly. The playground includes a JavaScript console where you can see debugging info like console.log()s and uncaught exceptions. Hopefully, you can get your script working entirely in the playground before pushing.

However, due to technical limitations, the playground doesn’t always exactly produce what will happen in production. If you run into problems not seen in the playground, console.log() does not log anywhere and uncaught exceptions produce a generic error page (error code 1101). We therefore recommend deploying your script to a test or “staging” site before making it live on your real site. You can set up a staging environment with a free workers.dev subdomain.

We are working to make this experience better, but for now, here are some tips for debugging on a live staging site:

1. Wrap your whole event handler in a try/catch

If you’re getting error code 1101 from your Worker, that means it is throwing an exception. You can catch the exception in your code using a regular old try/catch block. (Note that this works best if your code is factored into an async function, otherwise you also need to know how to catch exceptions from promises.)

async function handleRequest(request) {
  try {
    ... some logic that might error ..
    return await fetch(newUrl, request)
  } catch (err) {
    // Return the error stack as the response
    return new Response(err.stack || err)
  }
}

2. Return debug information in a header

A common quick hack to get some debug information out of your Worker is to return it as a header value in the response.

	...
    // Copy the response and initialize body to whatever info you want to log
    response = new Response(stack, response)
    // Shove our rewritten URL into a header to find out what it was.
    response.headers.set('X-Debug-stack', stack)
    response.headers.set('X-Debug-err', err)
  }
  return response
}

3. Setup a logging service

A Worker can make HTTP requests to any site on the public internet. Many projects already have a service like Sentry set up to collect error logs from browser-side JavaScript. You can use the same service to collect errors from your Worker, by making an HTTP request to the service to report the error. Refer to your service’s API documentation for details on what kind of request to make.

When logging using this strategy, you must account for the fact that outstanding asynchronous tasks are canceled as soon as a Worker finishes sending its main response body to the client. In order to ensure that a logging subrequest completes, you can pass its fetch() promise to event.waitUntil(). For example:

   ...
  // Without event.waitUntil(), our fetch() to our logging service may
  // or may not complete.
  event.waitUntil(postLog(stack))
  return fetch(request)
}

function postLog(data) {
  return fetch('https://log-service.example.com/', {
    method: 'POST',
    body: data,
  })
}

4. Go to Origin on Error

Often, if your Worker isn’t doing security-critical activities, it makes sense for it to act as if it’s not installed at all, if it errors. This allows you to install logging, tracking, or other minor Workers without fearing they may break your underlying site. This behavior can be toggled on by calling event.passThroughOnException():

addEventListener('fetch', event => {
  event.passThroughOnException()

  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  // An error here will return the origin response, as if the Worker wasn’t present.
  ...
  return fetch(request)
}

All together

Putting the 4 tips above together, you’d get a script like:

Debugging Tips

Send debug information in an errored response and to a logging service.

Copy into a Worker script:
const LOG_URL = 'https://log-service.example.com/' // Service setup up to receive logs async function handleRequest(event) { let response try { response = await fetch(event.request) if (!response.ok) { let body = await response.text() throw new Error( 'Bad response at origin. Status: ' + response.status + ' Body: ' + body.trim().substring(0, 10), //ensures is string that can be a header ) } } catch (err) { // Without event.waitUntil(), our fetch() to our logging service may // or may not complete. event.waitUntil(postLog(err)) const stack = JSON.stringify(err.stack) || err // Copy the response and initialize body to the stack trace response = new Response(stack, response) // Shove our rewritten URL into a header to find out what it was. response.headers.set('X-Debug-stack', stack) response.headers.set('X-Debug-err', err) } return response } addEventListener('fetch', event => { //Have any uncaught errors thrown go directly to origin event.passThroughOnException() event.respondWith(handleRequest(event)) }) function postLog(data) { return fetch(LOG_URL, { method: 'POST', body: data, }) }