All jobs

Fix Sentry Errors in a Laravel App

Yes. A Sentry alert starts a run; the agent reads the exception's frames, reproduces it with a Pest test in the Laravel app and opens a pull request with the fix.

Run this on RushIn a Project that has your repository.

What the agent does

The run starts when your handler receives a Sentry alert and sends this request with the alert's JSON after the prompt. The agent takes the issue id from that payload and asks the Sentry connection for the issue and its latest event: the exception class and message, the request URL and method, the user and release tags, and the in-app frames with their file, function and line. In a Laravel app the frames name files under app/, so the agent has a starting point before it opens the clone.

In an app like laravelio/laravel.io it then orients itself from composer.json: PHP 8.4, laravel/framework ^11.5, Livewire 3, Filament 4 and Pest 3 with the Laravel and Livewire plugins. Routing is in routes/web.php, and controllers hand work to single-purpose job classes under app/Jobs/ through dispatchSync, so a frame in ReplyController::store leads to App\Jobs\CreateReply, and one in ThreadsController::markSolution leads to MarkThreadSolution and then to Thread::markSolution in app/Models/Thread.php. That method throws CouldNotMarkReplyAsSolution when the reply's replyAble() is not a thread, and ReplyController::redirectToReplyAble aborts with a 500 for the same case, so those are the first lines the agent compares against the stack trace. bootstrap/app.php lists the exceptions the app does not report, which tells the agent whether the error should have reached Sentry at all.

Before it changes code the agent writes a failing test. The suite is Pest: tests/Pest.php, a TestCase with the CreatesUsers and HttpAssertions traits, tests/Feature/*Test.php for HTTP flows and tests/Integration/Jobs/*Test.php for jobs, each starting with uses(TestCase::class) and uses(RefreshDatabase::class). It follows that layout: a feature test that posts the same route the Sentry event recorded, using $this->login() and the Thread and Reply factories the way tests/Feature/ReplyTest.php and tests/Integration/Jobs/MarkThreadSolutionTest.php do, with an assertion on the response the app should have given instead of the exception. It runs that file alone with vendor/bin/pest tests/Feature/ThreadTest.php and keeps the failure output for the pull request.

Then it fixes the cause in the job or model, not by catching the exception in the controller, and reruns until the test passes. phpunit.xml sets DB_CONNECTION to mysql and tests.yml runs vendor/bin/pest -p against a mysql:5.7 service with DB_DATABASE=laravel, so the agent starts MySQL in its sandbox and runs the full suite the same way, after composer install, npm ci and npm run build as the workflow does. The Coding Standards workflow reuses Laravel's shared laravel/.github workflow, and laravel/pint is not a direct dependency in composer.json, so the agent runs Pint from a one-off install with Laravel's default preset on the files it touched, without adding it to the project.

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 Laravel app; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the request URL and the in-app stack frames. Find the failing code by following the frames from the controller in app/Http/Controllers to the job class in app/Jobs and the model method it calls. Write a Pest test in the matching tests/Feature or tests/Integration/Jobs file, using the existing factories and the login helpers from tests/CreatesUsers.php, that sends the same request and fails with the same exception. Fix the cause in the job or model rather than catching the exception, then run vendor/bin/pest -p against MySQL the way .github/workflows/tests.yml does, and Pint with Laravel's default preset on the files you changed, without adding it to composer.json. Open a pull request that links the Sentry issue, quotes the exception and frames, and shows the test failing before the fix. Change nothing unrelated to this error. If you cannot reproduce it, open no pull request and say what you tried.\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 on its own branch with two parts: the fix in a job class under app/Jobs/ or a model under app/Models/, and a new Pest test that fails on the code before the fix. The description links the Sentry issue, quotes the exception line and the frames the fix addresses, shows the test output before and after, and names each command the agent ran. The repository's own Tests and Coding Standards workflows run on the branch as on any pull request.

The session keeps every step: the Sentry calls, each pest and pint command with its full output, the MySQL it started and the files it read. GET /o/{org}/sessions/{id}/messages returns that transcript, and the run.completed webhook carries the agent's summary in data.result, so your handler can post the pull request link back on the Sentry issue.

Before merging, a reviewer checks that the test reproduces the exception Sentry reported rather than a neighbour, that the fix changes behaviour in the job or model and not by wrapping the controller action in a try, and that a change to the thread or reply flow keeps the policy checks in authorize intact. If the fix is wrong, a follow-up message to the same session asks for a different one and the agent continues on the same branch.

Run it on every Sentry issue alert

In Sentry, add an alert rule for the project whose action is a webhook, and point it at a small handler on your server. The handler sends this request with the alert's JSON after the prompt and the Sentry issue id as the Idempotency-Key header, so a resent alert returns the run it already started. One alert becomes one run and one pull request.

Set a webhook URL on the API key and Rush POSTs run.completed to it when the turn finishes, with the session_url and the result. Or have the handler call a webhook trigger on an automation in the Project with this prompt: it forwards the alert body with the trigger's secret as Authorization: Bearer, which Sentry cannot send itself. The body becomes the event text after the prompt, and max_runs_per_hour caps an issue that fires in bursts.

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

Why run it on Rush?

A Laravel exception from production usually means a MySQL database, a Composer install, a built Vite bundle and an hour of reading before the first test runs. The agent does that setup in its own sandbox, with the same MySQL version and Pest flags the workflow uses, and nothing on your machine changes while it works.

The pull request carries its evidence: a failing test, a passing test and a transcript of every command. Several Sentry issues can each get their own sandbox at the same time, within the key's max_concurrent limit, so the errors that surface after a deploy are each answered with a proposed fix rather than queued behind one person.

Frequently asked questions

How does the agent reach my Sentry project?

Connect Sentry once with POST /me/connections as an MCP server and name it in the run's connections field, as the request on this page does. The run gets only the connections it names, and every call it makes is in the transcript. Tell it in the prompt if it should never resolve or comment on an issue.

Does it need MySQL, or will SQLite do?

The app's phpunit.xml and tests.yml both run the suite on MySQL, so the agent starts MySQL in its sandbox and runs it the same way. Running on SQLite would risk passing a test that fails in CI on a MySQL-only query or collation.

The frames point at a Livewire or Filament class. Does it still work?

Yes. The agent follows the frames to the component under app/Livewire/ or app/Filament/ and reproduces the error with Pest's Livewire plugin, which composer.json already requires, the way tests/Feature/ReplyTest.php tests the EditReply component.

Will it add a global exception handler to make the error go away?

No. The prompt rules that out, and the diff would show any change to bootstrap/app.php. The fix has to change the job or model so the request succeeds or fails with a proper response, and the test has to prove it.

What happens when it cannot reproduce the exception?

It opens no pull request. Its final message says which route, request body and account state it tried, what the event's breadcrumbs showed, and what it would need, such as a request field Sentry scrubbed. You can answer in a follow-up to the same session and it continues.