# Turn Sentry Errors Into Fix Pull Requests for a Rust Service

Yes. A Sentry alert starts a run; the agent follows the frames into your Rust crates, reproduces the error with a cargo test and opens a pull request with the fix.

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

## What the agent does

A Rust service reports to Sentry either a panic, with the message and the frames of the thread that unwound, or an error event from its tracing layer. The agent reads the alert payload after the prompt, then uses the Sentry connection to fetch the issue and its latest event: the kind, the message, the frames with their crate and function names, and any tracing fields attached as tags. From those it decides which crate in the workspace to open.

In a Rust service like lldap/lldap the workspace in `Cargo.toml` has `server`, `app`, `migration-tool`, `set-password` and the crates under `crates/`: `ldap`, `graphql-server`, `domain`, `domain-handlers`, `domain-model`, `sql-backend-handler`, `auth`, `access-control`, `opaque-handler` and `validation`. `rust-toolchain.toml` and `rust-version` both pin 1.91.0, and the workspace uses edition 2024. The LDAP side is `crates/ldap/src/handler.rs`, whose `LdapHandler` exposes `do_bind`, `do_search_or_dse`, `do_extended_request`, `do_modify_request`, `create_user_or_group`, `delete_user_or_group`, `do_compare` and `handle_ldap_message`, with the operation bodies in `search.rs`, `modify.rs`, `create.rs`, `delete.rs`, `compare.rs` and `password.rs`. The GraphQL side is `crates/graphql-server/src/mutation/mod.rs`, with `create_user`, `update_user`, `update_group`, `add_user_to_group`, `delete_user`, `add_user_attribute` and the object-class mutations, and the queries under `query/`. Errors flow through `DomainError` in `crates/domain-model/src/error.rs`, and `server/src/ldap_server.rs` and `graphql_server.rs` wire both into actix.

The repository tests at the crate level, so the agent puts its test where the neighbours are. `handler.rs` holds the LDAP tests, built on `LdapHandler::new_for_tests` with a `mockall` backend and the `setup_bound_admin_handler`, `setup_bound_readonly_handler` and `setup_bound_password_manager_handler` helpers; the mutation module has its own `#[tokio::test]` cases such as `test_create_user_attribute_valid`. The agent adds a test that sends the request the Sentry event implies, a search filter, a modify on an attribute, or a mutation input, with the mock backend returning what the breadcrumbs show, and runs `cargo test -p lldap_ldap <name>` or the matching crate. The failing output is the reproduction and is kept.

Then it fixes the code, reruns the test until it passes, and runs what `.github/workflows/rust.yml` runs on the pinned toolchain: `cargo build --workspace`, `cargo test --workspace`, `cargo clippy --tests --workspace -- -D warnings` and `cargo fmt --check`. A change to a GraphQL type also needs `./export_schema.sh`, because the workflow diffs the exported schema against `schema.graphql` and fails when they differ. The agent does not replace a `?` with an `unwrap_or_default` to make a panic disappear; it returns the `DomainError` the caller already handles.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "A Sentry alert fired for this Rust service; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the frames with their crate and function names, and any tracing fields on the event. Find the failing code, starting from the LDAP handler in crates/ldap/src/handler.rs and its operation modules, and the GraphQL mutations and queries in crates/graphql-server/src. Add a #[tokio::test] in the module where the neighbouring tests live, using the mock backend helpers already there, that reproduces the error. Fix the code until the test passes, then run `cargo test --workspace`, `cargo clippy --tests --workspace -- -D warnings` and `cargo fmt --check`; run ./export_schema.sh if a GraphQL type changed. 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 crate and adds one test in that crate's existing test module. The description links the Sentry issue, names the function and the crate, shows the test failing on the old code and passing on the new, and lists the `cargo test`, `clippy` and `fmt` results. When a GraphQL type changed, the diff also includes the regenerated `schema.graphql`.

The transcript on the session keeps the Sentry reads, every `cargo` command with its full output and every file the agent opened, so the `-D warnings` result is visible rather than reported. The `run.completed` webhook delivers the agent's final message as `data.result` and a `session_url` to read the rest.

A reviewer checks that the test drives the same operation Sentry saw, that an LDAP change still answers the client with the right result code instead of closing the connection, and that a fix in a mutation keeps the permission checks the handler applied before it. A follow-up message to the session, when the fix belongs in the backend handler rather than the protocol layer, moves it on the same branch.

## Run it on every Sentry issue alert

Add an alert rule to the Sentry project with a webhook action and send it to a handler on your server. The handler appends the alert's JSON to this prompt and sends the request with the Sentry issue id as `Idempotency-Key`, so Sentry's retries find the run that already exists. Each issue gets one sandbox and one pull request.

With a webhook URL on the API key, Rush POSTs `run.completed` to your handler with `session_url` once the turn ends, 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 Rust panic by hand means building the workspace on the pinned toolchain, writing the mock setup for a handler you may not know, and waiting for clippy to agree. In a Rush sandbox the agent builds once on its own disk, uses the crate's existing mock helpers, and runs the same four commands the workflow runs, so the pull request arrives with the build already green.

The evidence travels with the result: a test that failed, the same test passing, and a transcript of each cargo command. Issues run in parallel, each in its own clone up to the key's `max_concurrent` limit, so an LDAP search bug and a GraphQL mutation bug from the same release are two small reviews instead of one long one.

## Frequently asked questions

### Which toolchain does the sandbox build with?

The one `rust-toolchain.toml` pins, 1.91.0, which is also the `MSRV` the workflow uses and the workspace's `rust-version`. The agent reads those and does not build with a newer compiler that might accept code the pinned one rejects.

### Does it need an LDAP client or a database to run the test?

No. The handler tests use `LdapHandler::new_for_tests` with a `mockall` backend, so the operation runs in process. The `sql-backend-handler` crate's tests use SQLite through sea-orm and need nothing outside the sandbox either; the agent runs them only when the fix is there.

### What if the panic is inside a dependency such as ldap3_proto or juniper?

The agent reads the frame above the dependency, the call in your crate that passed the bad value, and fixes or validates there. When no frame in your code is involved, it says so in its final message and opens no pull request, so a dependency upgrade can be decided separately.

### Will it edit schema.graphql by hand?

No. It runs `./export_schema.sh`, which the `rust.yml` workflow also checks by exporting the schema and diffing. A hand edit would drift from the code and fail that step.
