All jobs

Fix Sentry Errors in an Express App

Yes. A Sentry alert starts a run; the agent reads the stack trace, reproduces the error with a Jest test against the Express service and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The agent begins with the alert payload after the prompt and the Sentry connection: it fetches the issue and its latest event, reads the exception type and message, the request method, URL and query string, and the in-app frames with file, function and line. For a TypeScript Express service the frames point at the compiled output, so it keeps the function name and the route and matches them to the source by hand rather than trusting a line number.

In a service like gothinkster/node-express-realworld-example-app that source is small and regular. src/main.ts builds the app with cors, body-parser and the router from src/app/routes/routes.ts, which mounts the tag, article, profile and auth controllers under /api. Each controller in src/app/routes/<name>/<name>.controller.ts wraps its handler in try/catch and calls next(error), and the error middleware at the bottom of main.ts answers 401 for UnauthorizedError, the errorCode of an HttpException from src/app/models/http-exception.model.ts, or 500 with the bare message for anything else. A Sentry event with a 500 therefore means an error that was neither an HttpException nor a JWT failure: a Prisma error from src/prisma/prisma-client.ts, a TypeError on a missing body, or a bad number from Number(req.query.offset) reaching skip in getArticles.

The agent reads the service the frames name, such as getFeed, favoriteArticle or addComment in src/app/routes/article/article.service.ts, and writes the reproduction where the repository already tests services: src/tests/services/article.service.test.ts, which imports prismaMock from src/tests/prisma-mock.ts, a jest-mock-extended deep mock of the Prisma client reset before each test. The new test arranges the mock to return what the Sentry breadcrumbs show the database returned, calls the service with the request's arguments, and asserts the HttpException the route should have thrown instead. It runs that file with npx nx test api --testFile=article.service.test.ts and keeps the failure.

Then it fixes the service so the case becomes a 404 or 422 HttpException with the errors body the RealWorld spec uses, or guards the input before it reaches Prisma, and reruns until the test passes. It runs the whole suite with npx nx test api (Jest 29 through jest.config.ts and ts-jest), npx nx lint api, and a TypeScript build with npx nx build api, which project.json defines with esbuild. There is no workflow in .github/, so those three commands are the checks the pull request reports.

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 Express service; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the request method, URL and query, and the in-app frames. Match the frames to the controller in src/app/routes and the service function it calls. Add a test to the matching file in src/tests/services that uses prismaMock from src/tests/prisma-mock.ts to reproduce the database state the breadcrumbs show, calls the service with the same arguments, and asserts the HttpException the route should throw; confirm it fails with the Sentry error first. Fix the service so the case answers a proper status instead of a 500, keep the errors body shape the other routes use, then run npx nx test api, npx nx lint api and npx nx build api. 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

The result is a pull request on its own branch that changes one service file under src/app/routes/ and adds one test to src/tests/services/. The description links the Sentry issue, quotes the exception and the frames it maps to, pastes the failing test output and the passing rerun, and lists the nx test, nx lint and nx build commands with their results. If the cause was a missing guard in the controller rather than the service, the diff is in the controller and the description says why.

The session holds the full record: each Sentry call, every command with its output, and every file the agent opened. GET /o/{org}/sessions/{id}/messages returns it, and the run.completed webhook on the API key carries the final summary in data.result, so the handler that received the Sentry alert can post the pull request link back on the issue.

A reviewer checks that the test's mocked Prisma result matches what the breadcrumbs actually showed, that the new status code is the one the RealWorld API spec expects for that case, and that the error middleware in main.ts was not widened to hide other 500s. A wrong fix gets a follow-up message to the same session and a new commit on the same branch.

Run it on every Sentry issue alert

Give the Sentry project an alert rule with a webhook action pointed at a handler on your server. The handler sends this request with the alert JSON after the prompt and the Sentry issue id as the Idempotency-Key, so a redelivered alert returns the existing run. Each distinct issue becomes one run and one pull request.

Set a webhook URL on the API key and Rush POSTs run.completed or run.failed to it with the session_url. If you would rather keep the prompt in Rush, create an automation in the Project with a webhook trigger and have the handler forward the alert body to it with the trigger's secret as Authorization: Bearer; Sentry signs its own deliveries and cannot send that header. max_runs_per_hour on the automation stops a flapping issue from starting a run per event.

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

Why run it on Rush?

An Express 500 from production is usually a small fix hidden behind an afternoon of setup: a Postgres DATABASE_URL, prisma generate, the Nx cache, and then guessing which request produced the trace. The agent reads the event through your connection, reproduces it against the mocked Prisma client the repository already tests with, and does it in a sandbox that leaves your checkout alone.

What you review is a pull request with a failing-then-passing test and a transcript of every command, not a description of a fix. Issues that arrive together after a deploy each run in their own sandbox, up to the key's max_concurrent limit, and nothing merges until a person approves it.

Frequently asked questions

The frames point at dist/api/main.js, not the TypeScript. How does it find the line?

It uses the function name and the route from the event, then reads the controller and service in src/app/routes/ to find the matching code. project.json builds with esbuild and sourcemap: true in development, so when a source map is attached to the event it uses that directly.

Does the test need a real Postgres database?

No. The repository's service tests mock the Prisma client with jest-mock-extended in src/tests/prisma-mock.ts, and the new test follows that pattern. If the bug is in a Prisma query itself, the agent says so and starts Postgres in the sandbox to run the query against src/prisma/schema.prisma.

How does it get the Sentry issue?

Connect Sentry once with POST /me/connections and name it in connections on the run, as this page's request does. The run gets only the connections it names, and every call it makes appears in the transcript.

What if the right fix is to change the error middleware?

It can, but the prompt asks for a status from the route first and the pull request has to explain any change to src/main.ts. A reviewer sees that change in the diff and can push back in a follow-up message on the same session.