All jobs

Turn Sentry Errors Into Fix Pull Requests for a Cloudflare Worker

Yes. From a Sentry alert the agent finds the Worker handler that threw, reproduces it in workerd with a Vitest test on the bindings, fixes it and opens a PR.

Run this on RushIn a Project that has your repository.

What the agent does

A Sentry event from a Worker names the fetch handler, a Durable Object method or a route function, with frames in the bundled script. The agent reads the issue and its latest event through the Sentry connection, takes the request URL and method, the binding names in the message and the frames, and then opens the Worker's wrangler config and entry point in its clone before changing anything. A config with upload_source_maps set to true means the frames carry original positions, which is the case for every template in cloudflare/templates.

That repository is a pnpm workspace of every *-template directory plus a cli package, pinned to pnpm 10.2.0, with vitest 4.1.10, turbo 2.6.1, syncpack and prettier at the root. Each template also carries its own package-lock.json, generated by the templates CLI, and the root check script runs syncpack lint, templates lint, templates lint-npm-lockfiles, turbo run check cf-typegen and prettier before a git diff --exit-code. A template's own check script is tsc && wrangler deploy --dry-run on wrangler 4.136.1, and turbo.json makes check depend on build. The d1-template is the simplest shape: src/index.ts prepares SELECT * FROM comments LIMIT 3 on env.DB and renders the rows to HTML, wrangler.json declares the DB binding with observability enabled, and migrations/0001_create_comments_table.sql creates the table; its dev script applies the migrations to the local D1 first. Templates with tests use @cloudflare/vitest-pool-workers: to-do-list-kv-template has a vitest.config.ts built with defineWorkersConfig pointing at wrangler.jsonc, a test/env.ts that types env from cloudflare:test, and test/to-do-manager.test.ts that reads env.TO_DO_LIST, creates todos and deletes the key after each case.

The agent writes the reproduction in that pool. For a template that already has one, it adds a case to the existing test file; for one without, such as d1-template, it adds vitest and @cloudflare/vitest-pool-workers at the versions the other templates pin, since syncpack lint refuses a version that differs from the rest of the workspace, plus a vitest.config.ts with the template's wrangler config path. The test applies the D1 migration to the test database, seeds the rows or KV keys the event implies, such as an empty table, a missing key or a body with a field the handler never checked, and calls the handler through SELF.fetch or the exported class with env. It runs pnpm test in the template and keeps the failing output, which shows the same error as the Sentry event.

Then it fixes the cause, reruns the test until it passes, and runs pnpm run check in the template so tsc and wrangler deploy --dry-run still pass, followed by pnpm run check at the root for syncpack, the template linter, the npm lockfiles and prettier, and pnpm run fix when the linter can repair formatting itself. The fix is narrow: it does not raise compatibility_date or add nodejs_compat to make an error disappear unless the event shows that is the cause, and it does not catch an exception inside the handler and return 200. AGENTS.md in the repository asks for tests with every template change, and the pull request follows its checklist.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "A Sentry alert fired for this Cloudflare Worker; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the request URL and method, and the stack frames. Open the Worker's wrangler config and entry point and match the frames to the handler, Durable Object method or route that threw, and read the bindings it uses. Reproduce the error with a Vitest test running in workerd through @cloudflare/vitest-pool-workers: add vitest and the pool at the versions other packages in the workspace pin if the template has no test setup, with a vitest.config.ts pointing at the wrangler config, apply any D1 migrations to the test database, seed the bindings with the state the event implies, and call the handler through SELF.fetch or the exported class with env. Run pnpm test in the template until the fix makes it pass, then run the template's check script and pnpm run check at the root, applying pnpm run fix for formatting. Open a pull request that links the Sentry issue, quotes the frames and the failing test output, and explains the cause. If the error does not reproduce, stop and report what you tried. Change nothing unrelated to this error.\n\nAlert payload:\n{payload}",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ],
  "connections": [
    "sentry"
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

The result is a pull request with a small diff inside one template: the handler or class where the error started, a new or extended test file, and where the template had no runner, vitest and the Workers pool under devDependencies with a vitest.config.ts, plus the pnpm-lock.yaml and that template's package-lock.json. The description links the Sentry issue, names the file and line, shows the test failing before and passing after, and explains the cause in plain words, such as a D1 query on a table the migration had not created or a KV value parsed before its null check.

The session holds the Sentry queries, the install, each vitest run, the template check with its dry-run deploy, the root check and every file the agent read. Its final message, also delivered as result in the run.completed webhook, is the short account; the transcript is where a reviewer checks it.

Before merging, the reviewer confirms the test seeds the same binding state the event showed, that no compatibility flag or date changed without a stated reason, and that the root check passes so the template still lints and its lockfiles still match. A different approach is a follow-up message to the same session, and the agent amends the branch.

Run it on every new Sentry issue

Add an alert rule in Sentry for the Worker's project that fires when a new issue is created, with a webhook action aimed at a handler on your server, which can itself be a Worker. The handler sends this request with the alert's JSON after the prompt and the issue id as the Idempotency-Key, so Sentry's retries map to one run and each issue gets its own sandbox, test and pull request.

Give the API key a webhook URL and Rush POSTs run.completed when the turn ends, with session_url to read the pull request from. The handler can instead call a webhook trigger on an automation in the Project, forwarding the alert body with the trigger's secret as the bearer token, because Sentry signs its own requests and cannot send that header.

The use case this job follows, with its automation and trigger: Fix a bug from an alert.

Why run it on Rush?

A Worker error depends on binding state: the rows in D1, the keys in KV, the objects in R2 or a Durable Object's storage at the moment of the request. Reproducing that by hand means running workerd locally with seeded bindings and a test runner wired to the wrangler config, beside whatever else is on your machine. In a Rush sandbox the agent sets up the Workers pool on its own disk, seeds exactly the state the Sentry event shows, and leaves your environment alone.

The pull request carries the failing test, the passing test, the dry-run deploy and the full command record, so it can be checked rather than trusted. Each Sentry issue runs on its own, in parallel up to the key's concurrency limit, so a release that broke several templates or several Workers gets several reviewed fixes at once.

Frequently asked questions

How does the test reach D1, KV or a Durable Object?

Through @cloudflare/vitest-pool-workers, which runs the test inside workerd with the bindings declared in the wrangler config and exposes them as env from cloudflare:test. D1 migrations are applied to the test database in the setup, and KV keys are written and deleted around each case, as the to-do list template's test does.

The frames point into the bundled worker. Can it find the line?

Yes. The templates set upload_source_maps to true, so frames carry original file positions, and the agent names the handler and line in src. Without source maps it matches function names and the request route and says which frames it placed and which it inferred.

Will it change compatibility_date or compatibility flags?

Only when the Sentry event shows the error comes from a runtime behaviour those settings control, and then the pull request names the flag and the reason. The default fix lives in the handler and leaves the wrangler config alone.

Why run the root check as well as the template's own?

Because the repository's check script runs syncpack, the template linter, the npm lockfile check and prettier across every template, and a new dev dependency or a stray format change fails there even when the template's tsc and dry-run deploy pass. Running it in the sandbox means the pull request arrives with that already green.