# Fix fetch event handler crashed after returning response

## TL;DR

Move all async work that happens after the response into `ctx.waitUntil()`. In the Workers runtime, anything you await after returning the response gets killed when the isolate suspends, and the resulting rejection surfaces as a crash after the response. Wrap the background work in `ctx.waitUntil(promise)` and make that promise catch its own errors.

## Verbatim error

```text
workerd error: fetch event handler crashed after returning response
```

## Steps

1. Find the awaits that run after the `return` in your fetch handler (analytics writes, logging calls, fire-and-forget fetches). Expected: you locate the post-response async work.
2. Change the handler signature to accept the context: `async fetch(request, env, ctx)`. Expected: `ctx` is available in the handler.
3. Replace bare post-response awaits with `ctx.waitUntil(doWork())`, where `doWork()` is an async function holding the background task. Expected: the response still returns fast while the work continues.
4. Make `doWork()` catch its own errors internally (waitUntil rejections are logged, not fatal, but unhandled ones are noisy). Expected: no unhandled promise rejections in tail.
5. Load-test the endpoint and watch `wrangler tail`. Expected: response times stay low and no crash logs appear.

## Use this when

- workerd logs show "fetch event handler crashed after returning response"
- Analytics, logging, or cache writes happen after the response is returned
- Fire-and-forget fetches after `return` fail intermittently

## Not for this skill when

- The crash happens before the response is returned (debug the handler itself)
- You are streaming a response body (different lifetime rules apply)
- The code runs in a Durable Object (use `state.waitUntil` there)

## Variant phrasings

- worker crashed after respond
- waitUntil not working cloudflare workers
- background task killed after response returned
- promise rejected after response sent workers

## Why it happens

After the response is returned, the runtime may suspend the isolate at any moment. Promises that are not attached to `waitUntil` get cancelled, and their rejections surface as crashes attributed to the fetch handler. `ctx.waitUntil()` extends the isolate lifetime until the promise settles, which is the supported way to do post-response work.

## Edge cases

- `waitUntil` gives you up to 30 seconds of post-response execution; longer tasks get cut off.
- CPU-heavy background work still counts against the CPU time limit.
- Multiple `ctx.waitUntil()` calls are fine and run concurrently.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_mZIBoWitozo8B546bUF1RQ
