All jobs

Fix Sentry Errors in a Rails App

Send the Sentry issue to an agent: it reads the Rails stack trace, reproduces the error in a Minitest case, fixes the controller or job and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads the issue after the prompt and fetches its latest event through the Sentry connection: the exception class, the message, the transaction, the breadcrumbs and the in-app frames. In a Rails app the breadcrumbs are rich when the SDK is configured for it, and in codetriage/CodeTriage it is: config/initializers/sentry.rb sets breadcrumbs_logger to [:active_support_logger, :http_logger], excludes Sidekiq::Shutdown, and samples traces at 0.1, so the event carries the SQL, the cache hits and the outbound HTTP calls that led to the exception. app/jobs/sentry_job.rb sends events through a background job, which means a job error and a request error look alike until the transaction is read.

CodeTriage runs Rails 8.1.3.1 on Ruby 4.0.5 with Postgres, Puma, Sidekiq and Devise, with sentry-ruby and sentry-rails 7.0 in Gemfile.lock. A request transaction names a controller and action: repos#show, repos#list, repos#create, issue_assignments#click_issue_redirect, repo_subscriptions#update, users#update or the Devise omniauth_callbacks. A job transaction names one of the classes in app/jobs/: PopulateIssuesJob, UpdateRepoInfoJob, SendDailyTriageEmailJob, SendSingleTriageEmailJob, PopulateDocsJob or BackgroundInactiveEmailJob, most of which call GitHub through app/models/github_fetcher.rb and its classes.

The frames point at a controller, a model or a job, and the agent reads the chain around it. A NoMethodError on nil in repos#show usually traces to a repo whose GitHub data has not been populated; an error in IssueAssigner or issue_assignments#click_issue_redirect to an assignment whose issue was closed; an error in UpdateRepoInfoJob to a GitHub response the fetcher did not expect. The breadcrumbs show the HTTP calls, so the agent can see the response status that preceded the exception.

Tests are Minitest under test/: test/functional/repos_controller_test.rb, issue_assignments_controller_test.rb, repo_subscriptions_controller_test.rb and users_controller_test.rb for controllers, test/jobs/populate_issues_job_test.rb and update_repo_info_job_test.rb for jobs, test/unit/ for models, with fixtures loaded for all and GitHub stubbed through webmock and VCR cassettes in test/vcr_cassettes, as test/test_helper.rb sets up. The agent adds one test that drives the same request or job with the state the breadcrumbs describe, confirms it fails with the Sentry exception, fixes the code, runs bin/rake test for that file and then in full, as main.yml does after rake db:create db:schema:load, runs bundle exec standardrb on the changed files, and opens a pull request that links the Sentry issue and quotes the frames and the test output.

Start it with one API call

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

{
  "agent": "claude",
  "input": "A Sentry issue for this Rails repository is attached below. Read its latest event through the Sentry connection: the exception, the transaction (controller#action or job class), the breadcrumbs with SQL and HTTP calls, the release and the in-app frames. Open the controller, model or job the frames name under app/controllers, app/models or app/jobs and read the chain around the failing line. Add one Minitest case to the matching file under test/functional, test/jobs or test/unit that reproduces the request or job with the state the breadcrumbs describe, stubbing GitHub with webmock or a VCR cassette as the neighbouring tests do, and confirm it fails with the same exception. Fix the cause rather than rescuing it, run bin/rake test for that file and then the whole suite after rake db:create db:schema:load, run bundle exec standardrb on the files you changed, and open a pull request that links the Sentry issue, quotes the frames and the failing test output and explains the cause. Stop at needs_review if you cannot reproduce it.\n\nSentry issue:\n{issue}",
  "connections": [
    "sentry"
  ],
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  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 action, model method or job and adds one Minitest case, for example a test in test/functional/repos_controller_test.rb that requests a repo with no fetched issues and now expects a page rather than a 500. The description links the Sentry issue, names the transaction and the frame, states the cause, shows the test failing then passing, and gives the bin/rake test summary and the standardrb result.

The reviewer checks that the test uses fixtures and stubs the way its neighbours do rather than hitting GitHub, that the fix handles the state rather than rescuing the exception, and that a job change does not alter retry behavior in a way Sidekiq would amplify. A controller change should keep Devise's authentication filters as they were.

When the run ends needs_review, no pull request exists; the result explains what could not be reproduced, for example a GitHub response shape the cassettes do not contain, so a maintainer can supply it or close the issue.

Run it on every Sentry issue alert

Create an alert rule in the Sentry project with a webhook action pointed at a handler on your server. The handler sends this request with the issue JSON after the prompt and the Sentry issue id as Idempotency-Key, so a repeated delivery returns the run already started. A Rush webhook trigger wants Authorization: Bearer with its secret, which Sentry's own webhook cannot send; the handler adds it if you route through an automation in the Project instead.

Set a webhook URL on the API key and Rush POSTs run.completed or run.failed with the session_url when the turn finishes; the handler can comment the pull request link on the Sentry issue. An issue from a background job may fire on every Sidekiq retry, so the handler should dedupe on issue id.

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

Why run it on Rush?

Rails apps with background jobs produce Sentry issues that need a specific database state and a specific GitHub response to reproduce, and the maintainer of a volunteer project does not have an afternoon for each one. The agent builds the database from db:schema:load, loads the fixtures, stubs GitHub the way the suite already does, and hands back a pull request. Several issues can be several sessions at once.

The pull request is reviewable on its evidence: the event read, the Minitest case failing with the same exception, the full bin/rake test run afterwards and the standardrb check. A fix that only rescues the error would pass review nowhere, and the prompt forbids it.

Frequently asked questions

Does the agent need GitHub access to reproduce job errors?

No. test/test_helper.rb loads webmock/minitest and the suite keeps VCR cassettes under test/vcr_cassettes, so the agent stubs the GitHub response the breadcrumbs show. If the shape it needs is in no cassette, it writes a webmock stub in the test with that shape.

Which Ruby and database does the sandbox use?

Ruby 4.0.5 from .ruby-version with Bundler's cached gems, and a local Postgres created with rake db:create db:schema:load using config/database.ci.yml copied into place, the same steps as the main.yml workflow. Nothing touches a production database.

Will it run the whole suite?

Yes, after the single file passes: bin/rake test in full, as CI does, so a fix in a model does not break a controller test elsewhere. The pull request description shows both runs.

What if the error is in Devise or OmniAuth rather than app code?

The frames say so, and the fix stays in the app's side of it, for example app/controllers/users/omniauth_callbacks_controller.rb handling a missing email from GitHub. The agent does not patch gems; if the cause is in a gem, it says so and stops at needs_review.

Does it add the Sentry context to the fix?

Not unless the cause was missing context. The fix is the code change plus the test; the pull request description, not the code, carries the link to the issue and the quoted frames.