# Fix Next.js Exceptions From PostHog Error Tracking

Yes. An agent takes your most frequent PostHog exception, maps its frames to the team or billing code in your Next.js app, reproduces it and opens a pull request.

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

## What the agent does

The agent starts in PostHog. Through your PostHog connection it lists the project's error-tracking issues from the last 7 days, takes the one with the most occurrences, and reads its exception type and message, the sample stack frames, the URL of each event and when it was first seen. Events that all come from `/dashboard` and began after one deploy already narrow the search to one route group and one diff.

Then it maps the frames to a clone of your repository in its own sandbox. In nextjs/saas-starter (Next.js 15.6 canary, React 19.1, Drizzle on Postgres, Stripe 18, pnpm) the team settings page is the client component `app/(dashboard)/dashboard/page.tsx`: it fetches `/api/team` and `/api/user` with SWR, renders `teamData.teamMembers` with initials built by `getUserDisplayName(user).split(' ').map((n) => n[0])`, shows a remove button only for `index > 1`, and binds `removeTeamMember` and `inviteTeamMember` from `app/(login)/actions.ts` with `useActionState`. `app/api/team/route.ts` returns whatever `getTeamForUser` in `lib/db/queries.ts` finds, which is `null` for a visitor with no session. The billing side is `lib/payments/actions.ts`, whose `checkoutAction` and `customerPortalAction` are wrapped by `withTeam` from `lib/auth/middleware.ts` and throw `Team not found` when the user has no team; `lib/payments/stripe.ts` redirects to `/pricing` when the team has no `stripeCustomerId`, throws when the team's product is inactive or has no active prices, and in `handleSubscriptionChange` reads `(plan?.product as Stripe.Product).name` on the assumption that the product was expanded. `app/api/stripe/checkout/route.ts` turns every unexpected shape in the completed session into a thrown error and redirects to `/error`, and `app/api/stripe/webhook/route.ts` verifies the signature with `STRIPE_WEBHOOK_SECRET` before handling `customer.subscription.updated` and `deleted`.

Reading the code the frames point at, the agent looks for the assumption that real data broke: a member whose `name` and `email` are both empty, a team response of `null` reaching `.teamMembers`, a Stripe subscription whose product came back as an id string, a form that posted a role outside `member` and `owner` so `validatedActionWithUser` returned `result.error.errors[0].message`. The PostHog message, the URL and the first-seen time say which of these, or which other path, users actually hit, and the agent reads the commits that touched those lines in the same window.

The starter has no test runner: `package.json` lists only `dev`, `build`, `start` and the `db:*` scripts. So the agent adds Vitest and Testing Library as dev dependencies with pnpm, writes a test that renders the component or calls the action with the data the PostHog events show, with `fetch`, `@/lib/db/queries` or the `stripe` client mocked, and confirms it fails. It fixes the component, action or Stripe helper where the bad data enters, runs `npx vitest run` and `pnpm build` until both pass, and leaves `lib/db/migrations` and `drizzle.config.ts` alone. The pull request names the PostHog issue, its occurrence count and the frame that pointed at the bug.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Use the PostHog connection to list this project's error-tracking issues from the last 7 days and take the one with the most occurrences. Read its exception type, message, sample stack frames, event URLs and first-seen time. Map the frames to the source in app/(dashboard)/dashboard/page.tsx, app/(login)/actions.ts, app/api/team/route.ts, lib/payments/actions.ts, lib/payments/stripe.ts and the Stripe routes under app/api/stripe, and read the recent commits that touched those lines. The project has no test runner: add vitest and @testing-library/react as dev dependencies with pnpm, write a test that reproduces the exception with the data the PostHog events show (mock fetch, the queries module or the stripe client as needed), and confirm it fails. Fix the cause where the bad data enters, then run npx vitest run and pnpm build until both pass. Open a pull request that names the PostHog issue, its occurrence count and first-seen time, and quotes one sample stack trace with the line it maps to. If the cause is outside this repository, say so in the session result and open no pull request.",
  "connections": [
    "posthog"
  ],
  "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 named after the exception. In saas-starter it usually changes one file under `app/(dashboard)/dashboard/`, `app/(login)/` or `lib/payments/`, adds a test beside it, and adds `vitest`, `@testing-library/react` and a `test` script to `package.json` with the updated `pnpm-lock.yaml`. The description carries the PostHog issue, its occurrence count, the first-seen time, one sample stack trace and the source line it maps to.

The session transcript keeps the PostHog queries, the `pnpm` install, every `vitest` and `pnpm build` run with its output, and each file the agent opened. The `run.completed` webhook delivers the agent's final message with the `session_url`.

Before merging, a reviewer checks that the fix handles the bad data where it enters, in the API route or the action rather than with an optional chain at the crash site, that a change in `handleSubscriptionChange` or the checkout route still records the plan name the dashboard shows, and that the new test fails on `main`. After the deploy the PostHog issue should stop receiving events.

## Run it on every new top exception in PostHog

Run it from a daily scheduled job on your server: the job asks PostHog which issue has the most occurrences and sends this request only when that issue is new since its last run, with your PostHog connection named in `connections`. A fix that already shipped is not attempted twice, and a crash introduced by a release gets a pull request the next morning.

Set a webhook URL on the API key and Rush POSTs `run.completed` or `run.failed` with the `session_url` when the turn finishes. A key runs at most `max_concurrent` sessions at once, 4 by default, and answers `429` past that.

## Why run it on Rush?

Fixing a Next.js exception properly means reproducing it, and in a starter with no test runner that means choosing and wiring one before the first assertion, plus mocking Stripe and Postgres so the test does not need either. In a Rush sandbox the agent does that on its own machine, with its own `node_modules`, while your working copy and dev server stay untouched.

The result shows its work: the PostHog numbers it chose by, the trace and the source line, the test that fails before the fix, and every command with its output in the transcript. Several apps built from the same starter can each run in their own sandbox at the same time.

## Frequently asked questions

### Will the test hit Stripe or my database?

No. The test mocks the `stripe` client from `lib/payments/stripe.ts` and the functions in `lib/db/queries.ts`, so it needs neither `STRIPE_SECRET_KEY` nor `POSTGRES_URL`. The agent still needs `pnpm build` to pass, which compiles the routes without calling them.

### The app has no tests at all. Is adding Vitest in scope?

Yes, and the pull request keeps it small: the two dev dependencies, a `test` script and one test file. If you would rather keep the starter dependency-free, say so in the prompt and the agent reproduces the exception with a script instead and quotes its output.

### What if PostHog's frames are minified?

With source maps uploaded the frames name files and lines. Without them, the agent runs `pnpm build`, matches function names and the message to the source, and says in the pull request how confident the mapping is.

### Does it need the PostHog connection on every run?

Yes. A key run gets only the connections it names, so the request on this page lists `posthog`; connect it once with `POST /me/connections` and name it, or its id, on each run.
