# Fix Flutter Errors from Sentry Alerts

Yes. An agent reads the Sentry event from your Flutter app, reproduces it with a widget test, fixes the cause, runs analyze and flutter test, and opens a PR.

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

## What the agent does

The agent reads the alert payload after the prompt, then opens the issue and its latest event through the Sentry connection. A Flutter event carries the Dart exception type and message, frames with the file under `lib/` and the line, the device and OS tags, and the breadcrumbs the app recorded before the crash. It keeps the frame's file and line and the breadcrumb trail as the two facts the test must reproduce: which widget or provider threw, and what the user had done just before.

Then it reads the app. In an app shaped like simplezhli/flutter_deer, `pubspec.yaml` names `flutter_deer` 1.3.3+22 with `sdk: ">=3.4.0 <4.0.0"`, `provider` ^6.1.2, `fluro` ^2.0.5, `dio` ^5.9.0, `rxdart`, `sp_util` and `cached_network_image`, and the workflow in `.github/workflows/flutter-drive.yml` pins Flutter 3.47.4. Errors already flow through one place: `lib/util/handle_error_utils.dart` wraps `runApp` in `runZonedGuarded`, redirects `FlutterError.onError` into the zone in production, and leaves a `_reportError` function whose production branch is a comment saying to upload the error, which is where a Sentry SDK call lives in a real app. The network layer is `lib/net/dio_utils.dart`, which builds a `Dio` with `ResponseType.plain`, accepts every status with `validateStatus` and leaves status handling to `AdapterInterceptor` in `lib/net/intercept.dart`, and `lib/net/error_handle.dart`, whose `ExceptionHandle.handleException` maps a `DioException` to codes 1000 to 1007 and anything else to 9999 through `_errorMap`. Screens are grouped by module under `lib/` with a `page`, `provider` and sometimes `presenter` directory each; list screens extend `BaseListProvider<T>` from `lib/order/provider/base_list_provider.dart`, which calls `notifyListeners()` on every mutation, and presenters such as `lib/order/presenter/order_search_presenter.dart` fill the provider from `requestNetwork` callbacks, setting `hasMore` from `data.items!.length == 30`.

The agent matches the frame to one of those files and picks the test shape the repository already uses. A crash inside a widget gets a widget test in the style of `test/login/login_accessibility_test.dart`, which pumps `MyApp(home: page, theme: ThemeProvider().getTheme())` so routes, theme and localisation are real, then drives the breadcrumb trail with `tester.tap` and `tester.enterText` on the `Key`s the pages already carry, such as `phone`, `vcode` and `password` in the register page. A crash in a presenter or in the error mapping gets a plain `test` case, with `Dio` given a fake `HttpClientAdapter` that returns the body or status the breadcrumbs show, because the one network test the repository has, `test/net/dio_test.dart`, calls `api.github.com` for real and cannot run offline. Either way the test fails with the same exception and message as the event before any fix is made.

The fix goes to the one widget, provider, presenter or mapping at fault: a null check before a bang on a nullable model field, a `mounted` check before a `context` use after an `await`, a `_errorMap` entry for a `DioExceptionType` the extension maps to a code with no entry, a guard around `data.items!`. Then `flutter pub get` regenerates `DeerLocalizations` as `l10n.yaml` directs, `flutter analyze` runs under `analysis_options.yaml` with `strict-casts`, `strict-inference` and `strict-raw-types`, and `flutter test` runs the suite, with the network test named as skipped. The pull request quotes the event, the failing test output and the passing analyze and test output.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "A Sentry alert fired for this Flutter app; its payload follows this prompt. Use the Sentry connection to read the issue and its latest event: the Dart exception type and message, the frames with their file and line under lib/, the device tags and the breadcrumbs before the crash. Read pubspec.yaml, analysis_options.yaml, lib/util/handle_error_utils.dart, lib/net/dio_utils.dart and lib/net/error_handle.dart, then the module directory the frames point at. Write a failing test first in the style the repository already has: a widget test that pumps MyApp with the page and ThemeProvider().getTheme() and drives the breadcrumb steps through the widgets' Keys, or a plain test for a presenter or the error mapping with Dio given a fake HttpClientAdapter. Confirm it fails with the event's exception, then fix the cause in that one widget, provider, presenter or mapping without catching the exception to hide it. Run flutter pub get, flutter analyze and flutter test, naming any test that needs the network as skipped. Open a pull request that links the Sentry issue, quotes the exception, frames and failing test output, and explains the cause. If it does not reproduce, stop and report 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

What comes back is a pull request that changes one file under `lib/` and adds one test under `test/`, placed in the module directory the suite already uses, such as `test/order/` or `test/net/`. Its description links the Sentry issue, quotes the exception and the frames, shows the test failing before the change and passing after, and gives the `flutter analyze` and `flutter test` output with the Flutter version used and the network test named as not run. `pubspec.yaml` does not change.

Before merging, a reviewer checks that the test drives the breadcrumb trail rather than calling the throwing method directly, so the fix is proven at the level the user hit it; that a null check does not silently blank a screen the user expected to be filled; and that the analyzer passed under the strict settings, which the quoted output shows. If the event came from a release older than the branch and the line has moved, the description says so and the test pins the current behaviour.

## Run it on every Sentry issue alert

Create an alert rule in the Sentry project for the app with a webhook action pointed at a handler on your server. Sentry signs its own requests and cannot add the `Authorization: Bearer` header a Rush `webhook` trigger requires, so the handler receives the alert, places its JSON after the prompt and sends this request with the Sentry issue id as `Idempotency-Key`, which makes a redelivery return the run already started.

Set a `webhook_url` on the API key and Rush POSTs `run.completed` with the `session_url` when the turn ends, so the handler can read the pull request link and post it on the issue. A mobile crash arrives from many devices and Sentry folds them into one issue, so one issue is one run, and the handler can skip issues that already have an open pull request.

## Why run it on Rush?

A Flutter crash depends on a screen, a theme, a locale and a sequence of taps, and reproducing it by hand means a simulator and a guess. The sandbox pumps the real `MyApp` with the real theme in a widget test, drives the breadcrumb steps through the widget keys, and runs the strict analyzer and the suite with the pinned Flutter version, keeping every command's output on the session.

The pull request carries its own proof: the event the agent read, the test failing with that exception and passing after the change, and the analyze and test output a reviewer can check line by line. Several alerts become several sandboxes, and the reviewer judges a one-file diff instead of a stack trace.

## Frequently asked questions

### The repository has no Sentry SDK. Where does the event come from?

From your app, which reports with Sentry's Flutter SDK; the demo repository leaves a comment in `_reportError` where that call goes. The agent needs only the Sentry connection on the run to read the issue and its event, and the pull request does not add or configure the SDK.

### Does it need a device or simulator?

No. Widget tests run in the Flutter test binding without a device, and that is where the reproduction happens. The driver suite under `test_driver/` and the files under `integration_test/` need a device, so the agent does not run them and says so.

### How does it test code that calls the network?

By giving `Dio` a fake `HttpClientAdapter` that returns the body or status the breadcrumbs show, so `DioUtils`, the interceptors and `ExceptionHandle` run unchanged. The existing `test/net/dio_test.dart` calls GitHub's API for real, which the run names as skipped rather than counting as a failure.

### Will it change the error handling in handle_error_utils.dart?

Only if the frames point there. The usual fix is in the widget or provider that threw. A change to the zone handler would alter what every future crash reports, so the agent leaves it alone unless the event shows it is the cause.

### Which Flutter version does it use?

The one `flutter-drive.yml` pins, 3.47.4, since that is the version the project tests against; `pubspec.yaml` only requires 3.19.0 or later. The pull request names the version used, and the prompt can name another.
