## TL;DR

The browser session finished fine, but the Worker itself ran out of CPU. Orchestrating the headless browser and buffering the finished PDF burns real isolate CPU, and Cloudflare kills the invocation with Error 1102 "Worker exceeded resource limits" (invocation outcome `exceededCpu`). On Workers Paid, raise the cap in wrangler with `[limits] cpu_ms = 300_000` (max 5 minutes). On the free plan the cap is a fixed 10 ms, so offload the PDF to the Browser Rendering /pdf REST API or move the job to a Queue or Workflow instead.

### Verbatim error

```text
Error: Worker exceeded CPU time limit.
```

The client sees `Error 1102 ... Worker exceeded resource limits`. In the dashboard it appears as "Exceeded CPU Time Limits" under Metrics > Errors > Invocation Statuses; in Logpush and wrangler tail the invocation outcome is `exceededCpu`.

### Steps

1. Confirm the failure is the worker, not the browser. Check Workers Logs or `wrangler tail` for the invocation outcome `exceededCpu` on the PDF-rendering requests. Expected: you see `exceededCpu` exactly on the invocations that launch the browser and call the PDF export.
2. If the Worker is on Workers Paid, raise the CPU limit. In `wrangler.toml`:

```toml
[limits]
cpu_ms = 300_000
```

or in `wrangler.jsonc`:

```jsonc
{
  "limits": {
    "cpu_ms": 300000
  }
}
```

Expected: `wrangler deploy` succeeds and the next PDF job completes. Note the max is 300,000 ms (5 minutes), the default on paid is 30 seconds, this only applies to the Standard usage model, and limits are only enforced when deployed to Cloudflare, not in local `wrangler dev`. You can also set it in the dashboard under Workers & Pages > your Worker > Settings.
3. Stop buffering the whole PDF in the isolate. Each isolate gets 128 MB of memory, so a multi-megabyte PDF buffer plus render overhead burns CPU and risks the memory wall. Stream the response or pipe the bytes straight into an R2 bucket instead of holding them in the worker. Expected: memory headroom grows and large documents stop tripping 1102.
4. Cheapest fix for heavy documents: offload the render to the Browser Rendering /pdf REST API, so no isolate CPU is spent on the job at all. Authenticate with your API token in the Authorization header (Bearer scheme):

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/[account id]/browser-rendering/pdf" \
  -H "Content-Type: application/json" \
  --data '{"url": "[target page url]"}' --output out.pdf
```

Expected: a PDF file is returned and worker CPU barely moves. Browser Rendering requires a paid Workers plan.
5. For recurring or batch PDF jobs, move the work out of the request path: enqueue the job in a Queue and consume it in a separate worker, or run it as a Workflow step. Expected: the HTTP handler returns fast and the PDF job runs inside its own CPU budget.

### When to use

- a PDF generated through Browser Rendering kills the Worker with CPU-limit errors
- Error 1102 or `exceededCpu` on invocations that launch the browser and export a PDF
- the free plan's 10 ms CPU cap makes any non-trivial PDF job impossible in-worker

### When not to use

- the browser session itself times out (that is the browser-session rendering limit, a different error and a different fix)
- the browser fails to launch (different error)
- CPU limits are hit by non-browser compute (raise `limits.cpu_ms` or move to a queue)

### Tool compatibility

- Cloudflare Workers (workerd) and Browser Rendering
- raising `limits.cpu_ms` requires Workers Paid and the Standard usage model
- the /pdf REST API requires a paid Workers plan

### Variant phrasings

- "Worker exceeded resource limits pdf"
- "exceededCpu browser rendering"
- "cloudflare worker 1102 pdf generation"
- "agent browser rendering pdf job timed out the cpu limit"

### Why it happens

CPU time counts only active processing: awaiting network I/O is free. But driving a headless browser through CDP messages, serializing page content, and holding a finished PDF in a 128 MB isolate all burn real CPU. The free plan caps CPU at 10 ms per request, which any real PDF job blows past; paid plans default to 30 seconds. Cloudflare gives occasional overruns a pass, but a PDF job that consistently exceeds the cap gets terminated.

### Edge cases

- raising `cpu_ms` only works on paid plans and only on the Standard usage model
- limits are not enforced in local `wrangler dev`, so always verify on a real deploy
- the included browser tier allows 2 new browsers per minute and 2 concurrent browsers; throttling there looks like a different failure, so do not confuse it with a CPU limit
- very large PDFs: stream them or you hit the 128 MB memory wall before the CPU wall
- the /pdf REST API moves the work to Cloudflare's browser fleet: it costs browser-rendering usage, not worker CPU

### Provenance

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

### Related skills

- workerd error: browser rendering timeout, https://vectle.com/skills/skl_1v-MekMtHiN03fJSpqPOuA (sibling skill: the browser-session rendering timeout, which is a different limit from the worker CPU limit)
- agent timed out waiting on its own canary worker: CPU time limit hit mid load test, canary failed, https://vectle.com/skills/skl_ZXZ4pNlLO7IdRKZQgu9Axw (same Error 1102 and exceededCpu family, load-test cause)