# Turn Sentry Errors Into Fix Pull Requests for a NestJS API

Yes. From a Sentry alert the agent reads the trace, reproduces the failing controller or service call with a Jest test in your NestJS API, fixes it and opens a PR.

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

## What the agent does

A Sentry event from a NestJS API names a controller method, a service or a guard in its in-app frames, and the agent starts by matching those frames to the module tree. In an API like brocoders/nestjs-boilerplate that tree is under src: auth, auth-google, auth-apple and auth-facebook, users, session, files, mail, roles and statuses, each with a controller, a service, DTOs and an infrastructure folder that holds a relational TypeORM adapter and a document Mongoose adapter behind one repository interface. The agent reads the frames against src/auth/auth.service.ts and src/users/users.service.ts first, because that is where the request path and the exception paths meet.

It reads package.json next: @nestjs/common and @nestjs/core 11.1.18, typeorm 0.3.28 with pg 8.20.0, mongoose 9.4.1, class-validator 0.15.1, nestjs-i18n, jest 30.3.0 with ts-jest and supertest 7.2.2, Node 24.14.1 from .nvmrc and npm from package-lock.json. The unit config in package.json points Jest at src with a .spec.ts pattern, and there are no spec files there, so the first test the agent writes is the first unit test in the project. The e2e config in test/jest-e2e.json runs .e2e-spec.ts files with supertest against a running server at APP_URL, port 3001 from env-example-relational, with Postgres and a maildev container started by docker-compose.relational.test.yaml and seeded by startup.relational.test.sh. main.ts explains the response shapes the event shows: a global ValidationPipe from src/utils/validation-options.ts turns validation failures into 422 with an errors tree, versioning is by URI, and ClassSerializerInterceptor shapes every response.

The test depends on where the frame sits. For a frame in a service the agent writes auth.service.spec.ts with Test.createTestingModule, mocking UsersService, SessionService, MailService, JwtService and ConfigService, and calls the method with the input the event implies: a login for a user whose provider is not email, which validateLogin answers with the 422 built by invalidLoginException, or a social login whose profile has no email. For a frame in a controller, a guard or an interceptor it writes an .e2e-spec.ts under test that registers a user through /api/v1/auth/email/register and replays the request with supertest, with Postgres started in the sandbox and the migrations and seeds run as startup.relational.test.sh does. It runs npm test or npm run test:e2e and keeps the failing output, which shows the same exception as the Sentry event.

Then it fixes the cause, reruns until the test passes, and runs npm run lint and npm run build. It follows CLAUDE.md in the repository: entities, schemas and properties are generated with the hygen scripts rather than written by hand, so a fix that needs a new column goes through npm run add:property:to-all-db and the generated migration. It does not change validation-options.ts or the global pipe to hide the error, and it does not swap a 422 for a 500 to make a test green.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "A Sentry alert fired for this NestJS API; its payload follows this prompt. Use the Sentry connection to read the issue, its latest event, the request URL and method, and the in-app stack frames. Match the frames to the controllers and services under src, starting with src/auth/auth.service.ts and src/users/users.service.ts, and to the global pipe and interceptors in src/main.ts. Reproduce the error with a failing Jest test: a unit spec beside the service built with Test.createTestingModule and mocked providers when the frame is in a service, or an e2e spec under test run with supertest against the API started with Postgres in the sandbox when the frame is in a controller, guard or interceptor. Fix the cause until the test passes, then run npm run lint and npm run build. Follow CLAUDE.md and use the generators for any entity or property change. Open a pull request that links the Sentry issue, quotes the frames and the failing test output, and explains the cause. If the error does not reproduce, stop and report what you tried. 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

The result is a pull request with a small diff: the service or controller where the exception started, a new spec file beside it or a new e2e spec under test, and nothing else unless a generator added a property and its migration. The description links the Sentry issue, names the method and line, shows the test failing before and passing after, and explains the cause in plain words, such as a social profile without an email reaching a path that assumed one.

The session keeps the full record: the Sentry queries, npm ci, the Jest runs with their output, the lint and build, and the Postgres start when the e2e path was used. The final message is the summary a reviewer reads first and the result the run.completed webhook carries.

Before merging, the reviewer confirms that the test hits the code path Sentry reported, that a change in a thrown status is the status the client should get and not a way to silence the alert, and that both the relational and document adapters still compile when the fix touched a repository interface. A different approach is a follow-up message to the same session, and the agent amends the branch.

## Run it on every new Sentry issue

Add an alert rule in Sentry for the API's project that fires when a new issue is created, with a webhook action aimed at a handler on your server. The handler sends this request with the alert's JSON after the prompt and the issue id as the Idempotency-Key, so Sentry's retries map to one run and each issue gets its own sandbox, test and pull request.

Give the API key a webhook URL and Rush POSTs run.completed when the turn ends, with session_url to read the pull request from. The handler can instead call a webhook trigger on an automation in the Project with this prompt, forwarding the alert body with the trigger's secret as the bearer token, because Sentry signs its own webhooks and cannot send that header.

## Why run it on Rush?

Reproducing a NestJS error by hand means standing up Postgres and maildev, running migrations and seeds, and replaying a request with the exact body the event shows, all beside whatever is already running on your machine. In a Rush sandbox the agent starts those services on its own disk, runs the first Jest spec the project has had, and leaves your environment alone.

What comes back can be checked rather than trusted: the failing test, the passing test and the transcript of every command. Each Sentry issue runs on its own, in parallel up to the key's concurrency limit, so a release that opened several issues gets several reviewed fixes at once.

## Frequently asked questions

### There are no unit tests in src. Does the agent still write one?

Yes. The Jest config in package.json already looks for .spec.ts files under src, so the agent adds the first one beside the service with Test.createTestingModule and mocked providers. The e2e suite under test is used when the error lives in a controller, guard or interceptor that a unit test cannot reach.

### Does it need Docker for the e2e path?

It needs Postgres and a mail catcher reachable from the API, which the repository starts with docker-compose.relational.test.yaml. In the sandbox the agent can use that compose file or start Postgres directly and point DATABASE_HOST at it, then run migration:run and seed:run:relational as startup.relational.test.sh does.

### Which database variant does it test against?

The one your deployment uses, read from DATABASE_TYPE in your environment file. The repository ships both a TypeORM adapter and a Mongoose adapter behind one repository interface, so a fix in a service is checked against the adapter the event came from and compiled against both.

### Will it change the 422 error format?

No. validation-options.ts and invalidLoginException define the errors tree clients depend on, and the fix keeps it. If the correct status for the failing case differs from what the code throws today, the pull request says so and the reviewer decides.
