# Turn Sentry Errors Into Fix Pull Requests for a Spring Boot App

Yes. A Sentry alert starts a run; the agent reads the exception, reproduces it with a JUnit test against your Spring Boot controllers and opens a pull request.

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

## What the agent does

A Spring Boot exception in Sentry arrives with the exception class, the message, the request path and a Java stack trace where the in-app frames name a controller or service method and the rest is the framework. The agent reads the alert payload after the prompt, fetches the issue and its latest event through the Sentry connection, and keeps the in-app frames, the request method and path, and the tags. Then it opens its clone and reads the methods the frames name.

In a Spring Boot app like spring-projects/spring-petclinic, `pom.xml` inherits `spring-boot-starter-parent` 4.1.0 with `java.version` 17, and `build.gradle` mirrors it; the `maven-build.yml` workflow runs `./mvnw -B verify` on JDK 17 and `gradle-build.yml` runs `./gradlew build`. The `validate` phase runs `spring-javaformat:validate`, so a file that is not formatted fails the build before any test runs, and checkstyle with nohttp runs too. The controllers under `owner/` are small: `OwnerController.showOwner` loads the owner with `findById` and throws `IllegalArgumentException` through `orElseThrow` when there is none, `processFindForm` strips the last name, redirects when `page` is out of range and rejects the field with `notFound` on an empty result, and `VisitController.loadPetWithVisit`, a `@ModelAttribute` method that runs before both visit handlers, throws `IllegalArgumentException` for a missing owner or pet and otherwise adds a new `Visit` to the pet. `processNewVisitForm` rejects a date that is not in the future with the `typeMismatch.visitDate` message. `CrashController` maps `/oups` to a deliberate `RuntimeException`; the agent recognises it as the showcase it is and does not fix it.

The test goes in the matching `@WebMvcTest` class. `OwnerControllerTests` and `VisitControllerTests` use `MockMvc` with the repository as a `@MockitoBean`, so a new test stubs `owners.findById` to return the owner the Sentry event implies, or empty, performs the request from the event's path and method, and asserts the status and view the code should produce. The agent runs `./mvnw -Dtest=VisitControllerTests test` and keeps the failure as the reproduction. Tests that need a database, `PetClinicIntegrationTests` on H2, `MySqlIntegrationTests` on Testcontainers and `PostgresIntegrationTests` through Docker Compose, stay out of the loop unless the fix reaches a repository or an entity.

Then it fixes the controller or the model, formats with `./mvnw spring-javaformat:apply`, reruns the test until it passes and runs `./mvnw -B verify`, the workflow's command, which runs format validation, checkstyle, the full test suite and jacoco. If an exception should become a 404 rather than a 500, the agent adds a `@ResponseStatus` or an `@ExceptionHandler` next to the controller instead of catching it inside the handler, and it does not change the `ModelAttribute` order that `VisitController` relies on.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "A Sentry alert fired for this Spring Boot app; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the request path and the in-app stack frames. Find the failing method, starting with OwnerController and VisitController in src/main/java/org/springframework/samples/petclinic/owner. Add a test to the matching @WebMvcTest class under src/test/java, stubbing the repository as a @MockitoBean and performing the request from the event, that reproduces the exception. Fix the code until the test passes, run `./mvnw spring-javaformat:apply`, then `./mvnw -B verify`. Open a pull request that links the Sentry issue, quotes the frames and the failing test output, and explains the cause. Leave CrashController alone; it throws on purpose. 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

You get a pull request with the fix in one controller or model class and a new `@Test` method in its `@WebMvcTest` class. The description links the Sentry issue, quotes the exception and the in-app frames, shows the new test failing before the fix and passing after, and reports that `./mvnw -B verify` passed with the format and checkstyle validation included. Your own workflows run on the branch as they do on any pull request.

The session's transcript keeps the Sentry reads, each `mvnw` command with its full Surefire output, and the files the agent opened. The `run.completed` webhook carries the agent's final summary as `data.result` and a `session_url`, so a handler can post the pull request link to the issue or to the team.

Before merging, a reviewer checks that the stubbed repository state matches what the Sentry event showed, that a status change is declared at the controller level rather than hidden in a catch, and that the Thymeleaf view the handler returns still receives the model attributes it reads. A follow-up message to the same session asks for a different approach and the branch is updated in place.

## Run it on every Sentry issue alert

Add an alert rule to the Sentry project with a webhook action and aim 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 from Sentry map to the one run. One issue becomes one sandbox and one pull request.

Give the API key a webhook URL and Rush POSTs `run.completed` with `session_url` when the pull request is ready. 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?

A Spring Boot fix on a laptop is mostly waiting: the Maven build, the format plugin rejecting a tab, the full suite with Testcontainers. The agent does that in its own sandbox with JDK 17, runs the one test first and the whole `verify` last, and keeps every line of output, so the reviewer sees a green build instead of reading that there was one.

The pull request carries its proof, a test that failed and now passes, and the transcript shows each command behind the claim. Issues run in parallel in separate clones, up to the key's `max_concurrent` limit, so the exceptions a release left in Sentry become several small reviewed branches instead of one person's queue.

## Frequently asked questions

### Maven or Gradle?

Both builds exist and CI runs both; the agent uses `./mvnw`, the wrapper the Maven workflow runs, and keeps the change buildable with `./gradlew build` too. If your project has only one, it uses that.

### Will the format plugin fail my build after the fix?

No. `spring-javaformat:validate` runs in the `validate` phase, so the agent runs `./mvnw spring-javaformat:apply` before `verify` and the diff arrives formatted. Checkstyle with nohttp runs in the same build.

### Does it start MySQL or Postgres for the tests?

Only when the fix touches a repository or entity. The `@WebMvcTest` classes mock the repository, and `PetClinicIntegrationTests` runs on H2 from `application.properties`; `MySqlIntegrationTests` uses Testcontainers and `PostgresIntegrationTests` uses Docker Compose, both of which the sandbox can run when the prompt allows it.

### What if the exception is the one /oups throws on purpose?

`CrashController` exists to demonstrate the error page, and the prompt tells the agent to leave it alone. It opens no pull request for that issue and says so, and you can mute the issue in Sentry.
