All jobs

Fix a FastAPI App Bug From a Datadog Monitor Alert

Yes. An agent reads the Datadog monitor, logs and traces behind an alert, finds the FastAPI route at fault, fixes it with a pytest test and opens a PR.

Run this on RushIn a Project that has your repository.

What the agent does

The agent begins with the monitor payload your server passes in and asks Datadog, through its MCP server, for the rest: the monitor's query and thresholds, the error logs for that service and environment in the half hour around the alert, and the APM traces for the failing requests. For a FastAPI service, a trace's resource name carries the route template, such as GET /api/v1/items/{id}, and its error span carries the exception type and the Python traceback. The agent groups those by the innermost frame in your own code, so ten thousand identical 500s become one problem with a sample request.

It then finds the route. In an app built on fastapi/full-stack-fastapi-template the routers live in backend/app/api/routes/ (items.py, users.py, login.py, private.py, utils.py), mounted under API_V1_STR = "/api/v1" from app/core/config.py. A failing GET /api/v1/items/{id} maps to read_item, which calls session.get(Item, id) and raises HTTPException(status_code=404, detail="Item not found") or 403 with "Not enough permissions". An unhandled 500 there means something before those checks broke, so the agent reads the SQLModel models in app/models.py, the helpers in app/crud.py and the latest Alembic revision before it decides where the fault is.

It reproduces the failure as a pytest test before it fixes anything. The template's suite in backend/tests/ uses FastAPI's TestClient, a session-scoped db fixture that calls init_db and deletes every Item and User at the end, and superuser_token_headers and normal_user_token_headers fixtures, so the new test in tests/api/routes/test_items.py or test_users.py sends the same request the trace shows, with the same kind of user. The tests need Postgres (compose.yml runs postgres:18), so the agent brings the database up, runs alembic upgrade head and python app/initial_data.py the way scripts/prestart.sh does, and installs with uv for the Python 3.14 that .python-version names.

With the test failing for the same reason the alert fired, the agent makes the smallest fix that handles the input class, runs the new test, then bash scripts/tests-start.sh, which runs the whole suite under coverage. The template's CI fails under 90 percent coverage (coverage report --fail-under=90), so the test also keeps the new branch covered.

Start it with one API call

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

{
  "agent": "claude",
  "input": "A Datadog monitor on this FastAPI service fired; its webhook payload is at the end of this prompt. Use the Datadog connection to read the monitor, the error logs and the APM traces for its service and environment from 15 minutes before to 15 minutes after it fired. Group the errors by exception and the innermost frame in this repository, map each trace's route template to its router in backend/app/api/routes, and find the code path that produced them. Start the database the tests need, apply the Alembic migrations, and write a pytest regression test with the existing TestClient and token fixtures that sends the request the traces show and fails for the same reason. Fix the cause, run the full backend test suite, and open a pull request that quotes the log lines and trace ids the fix addresses. If the cause is not in this code, change nothing and answer with what the logs show.\n\nPayload: <Datadog webhook body>",
  "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

When the fault is in the service, the agent opens a pull request with the fix in the router, CRUD helper or model it traced, and a regression test in backend/tests/api/routes/. Its description names the monitor, quotes the Datadog log lines and trace ids it reproduced, shows the request the test sends, and lists the pytest and coverage commands with their result. If the fix needs a schema change, the pull request includes a new Alembic revision and says so at the top.

When the logs show a cause outside the code, such as Postgres refusing connections or a missing SECRET_KEY in one environment, the run makes no change and its answer explains what Datadog showed and where to look. That answer arrives on your webhook with the run, so the alert channel still learns what happened.

Before merging, a reviewer checks that the test fails on the current main for the alert's reason, that the fix does not widen what a normal user can read or change (the 403 checks in items.py are the line to watch), and that any migration runs forward cleanly on a copy of production data.

Run it on every Datadog error-rate alert

Create a webhook in Datadog's webhook integration pointing at your server and mention it in each monitor you want handled, such as an error-rate monitor on service:backend. When it moves to Alert, your server adds the payload to this prompt and sends the request with the repository in resources. Ignore Recovered and No Data transitions so only real failures start a run.

Rush POSTs run.completed or run.failed to the webhook URL set on your API key when the turn ends, with the answer and the session URL, which your server can forward to the incident or the on-call channel. Keep the alert id with the returned execution_id so a monitor that re-notifies during the same incident does not start a second run.

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

Why run it on Rush?

Reproducing a production error locally means matching Python, uv, Postgres and migrations to what the service runs, and that is exactly the work nobody wants to do at night. The agent does it in its own sandbox: its own clone, its own database, its own Datadog session. Your laptop and your local database are not involved, and the run continues whether or not anyone is awake.

Every claim in the pull request can be checked. The trajectory keeps each Datadog query and its response, each migration and each pytest run with its output, and the regression test proves the fix against the request the traces captured. Several alerts in one incident each get their own run and their own sandbox, and a person still decides what merges.

Frequently asked questions

How does the agent read Datadog?

With Datadog's own MCP server, one of the connectors Rush lists. Connect it once through POST /me/connections with "type": "mcp" and OAuth, sign in, and add "connections": ["datadog"] to the request body. The listed server is the US1 site; another Datadog site is added as your own MCP server with its URL.

Will it touch my production database?

No. It reads logs and traces through Datadog, and runs the tests against a database it starts in its own sandbox. It never needs your database credentials for this job.

We use Sentry for errors. Does that matter?

The template initialises sentry_sdk when SENTRY_DSN is set, and that can run beside Datadog. This job reads Datadog; if your errors are only in Sentry, connect Sentry's MCP server instead and change the prompt to read issues from it.

What if the route is async and the error is a timeout?

It reads the trace's span durations to find which call ran long, such as a query or an outbound httpx request, and checks whether the code awaits it without a limit. A timeout caused by load or an upstream outage is reported in the answer rather than patched over with a longer timeout.