# Turn Sentry Errors Into Fix Pull Requests for a Phoenix App

Yes. A Sentry alert starts a run; the agent reads the Elixir stack trace, reproduces it with an ExUnit test against your Phoenix app and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/fix-sentry-errors/phoenix/), in a Project that has your repository.

## What the agent does

A Phoenix app that uses the `sentry` package reports an exception with the module, function and arity of each frame, the Plug route, and the request id, so the agent has an exact starting point. It reads the alert payload after the prompt, fetches the issue and its latest event through the Sentry connection, and keeps the exception, the frames, the route and the breadcrumbs. Then it opens its clone of the repository and reads the controller action and the context functions the frames name.

In a Phoenix app like hexpm/hexpm, `mix.exs` requires Elixir ~> 1.20 and lists `{:sentry, "~> 13.0"}`; `config/config.exs` points the SDK at `Hexpm.SentryClient`, a Finch-based HTTP client, and `config/prod.exs` turns on source code context and a `before_send` in `Hexpm.Application` that drops websocket protocol errors, anything whose `Plug.Exception.status` is under 500 and a list of metric-only classes, with Oban errors captured through the integration. That tells the agent what reached Sentry was a real 500 or a job failure. The publish path is `lib/hexpm_web/controllers/api/release_controller.ex`: `publish/2` runs behind `fetch_body` with a tarball size limit, `parse_tarball`, `maybe_fetch_package` and an `authorize` plug that allows workload identities, calls `release_metadata/2`, which checks `Hexpm.TarballLimits` and unpacks the metadata into a temporary directory, logs the tarball under `debug/tarballs` with a random id, and hands the result to `Releases.publish/8`, an `Ecto.Multi` in `lib/hexpm/repository/releases.ex` that creates the package and release, matches security advisories, recomputes dependants and writes the audit log inside one transaction. `publish_result/2` renders 201 with a `location` header, a validation failure or a changeset error. `create/2`, `show/2` and `delete/2` sit beside it, and `package_controller.ex` serves `index`, `show` and `audit_logs`.

The test goes next to the existing ones in `test/hexpm_web/controllers/api/release_controller_test.exs`, which uses `HexpmWeb.ConnCase` with `async: true`, ex_machina `insert/2` factories for users, repositories, packages and releases, and `create_tar/1` from `test/support/tarball_helpers.ex` to build a publishable tarball from a metadata map. The agent writes a test that posts the tarball or request the Sentry event implies, a metadata map with the field that broke, or a package name that does not match the URL, and asserts the response the code should give. `mix test test/hexpm_web/controllers/api/release_controller_test.exs:<line>` runs that one case; `test/test_helper.exs` starts the in-memory store, puts the SQL sandbox in manual mode and builds the registry, and `config/test.exs` keeps every bucket in `Hexpm.Store.Memory`.

Then it fixes the controller or the context, reruns the test until it passes, and runs `mix format --check-formatted` and `mix test`, the two commands the `main.yml` workflow runs on OTP 29.1 and Elixir 1.20.4 with a Postgres service. The suite needs Postgres with the `pg_trgm` and `pgcrypto` modules, as the README says, and `mix setup` creates and seeds it; the sandbox runs Postgres for this. The agent does not add a `rescue` in the action to turn an exception into a 422; it validates the input where `release_metadata/2` already validates, or fixes the function that assumed a field was present.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "A Sentry alert fired for this Phoenix app; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the request route and the stack frames with their module, function and arity. Find the failing code, starting with the API controllers under lib/hexpm_web/controllers/api, in particular release_controller.ex and package_controller.ex, and the context functions they call under lib/hexpm. Add an ExUnit test to the matching *_controller_test.exs under test/hexpm_web/controllers/api, using the ConnCase, the ex_machina factories and the tarball helpers already there, that reproduces the exception. Start Postgres, run `mix setup`, fix the code until the test passes, then run `mix format --check-formatted` and `mix test`. Open a pull request that links the Sentry issue, quotes the frames and the failing test output, and explains the cause. 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

What comes back is a pull request that changes one controller or context module and adds one `test` block to the controller test that already covers it. The description links the Sentry issue, quotes the exception and the frames with their module and function, shows the test failing before the fix and passing after, and reports the `mix format --check-formatted` and `mix test` results. Your own CI runs on the branch as on any pull request.

The session keeps the transcript: the Sentry reads, the `mix setup` and `mix test` commands with their full output, and the files the agent opened, so a reviewer sees the suite's own summary line rather than a sentence about it. The `run.completed` webhook carries the agent's final message as `data.result` and a `session_url` for the rest.

Before merging, a reviewer checks that the new test sends what the Sentry request showed and not a convenient variant, that a validation added in the controller does not duplicate one the changeset in `lib/hexpm` already performs, and that a change inside the publish `Multi` still runs in the one transaction. A follow-up message to the same session changes the approach on the same branch.

## Run it on every Sentry issue alert

Give the Sentry project an alert rule with a webhook action and point it at a handler on your server. The handler sends this request with the alert's JSON after the prompt and the Sentry issue id as `Idempotency-Key`, so retries return the run already started. One issue, one sandbox, one pull request.

With a webhook URL on the key, Rush POSTs `run.completed` to your handler with `session_url` when the turn finishes, and the handler can comment the pull request link on the issue. Or have that handler call a `webhook` trigger on an automation in the Project with this prompt: it forwards the alert's JSON as the body with the trigger's secret as `Authorization: Bearer`, because Sentry signs its own webhooks and cannot send that header. The body becomes the event text after the prompt, and `max_runs_per_hour` caps an issue that keeps firing.

## Why run it on Rush?

Reproducing a Phoenix exception by hand means a Postgres with the right extensions, `mix setup`, a tarball built the way the test helpers build it, and the patience to wait for the suite. In a Rush sandbox the agent does all of that on its own disk with the OTP and Elixir versions the workflow names, and your checkout and database stay as they were.

The pull request arrives with its proof, a test that failed and now passes, and the session transcript shows every command behind it. Each Sentry issue is its own run in its own clone, in parallel up to the key's `max_concurrent` limit, so the errors a deploy leaves behind are several small reviews instead of one long triage.

## Frequently asked questions

### Does the sandbox need Postgres to run mix test?

Yes. The suite uses `Ecto.Adapters.SQL.Sandbox` against a real database, and the README asks for the `pg_trgm` and `pgcrypto` modules. The agent starts Postgres in the sandbox and runs `mix setup` first, the same shape as the `main.yml` workflow's Postgres service.

### Which Elixir and OTP versions does it use?

The ones the workflow pins, Elixir 1.20.4 on OTP 29.1, which satisfy the `~> 1.20` requirement in `mix.exs`. The agent reads both files and says in the pull request which versions ran the tests.

### What if the error came from an Oban job rather than a request?

The Sentry event says so, because `config/prod.exs` captures Oban errors through the integration with `report_oban_error?/2` as the filter. The agent then tests the worker module directly, with the job args the event carried, instead of a controller.

### Will it change the Sentry configuration itself?

No. `before_send` already filters 4xx responses and known noise, and that is a decision for you. If the agent finds that the exception should have been filtered, it says so in the pull request and leaves `config/prod.exs` alone.
