Repository: flutter
Issue: [go_router] GoRouterState.error is only populated for errorBuilder when the route is not found
Labels: package,has reproducible steps,P2,p: go_router,team-go_router,triaged-go_router,found in release: 3.24,found in release: 3.27

### Steps to reproduce

The `GoRouterState` object has an `error` property which I would expect to hold a `GoException` when I open a non-existing route. Currently this exception is only available in `errorBuilder` and `errorPageBuilder`, but always `null` in `onException` and `redirect`.

My use case would be: I try to protect most of the screens with authentication by the `redirect` callback, but I can't make error screens unprotected as `state.error` is always `null` and nothing else indicates that I am on an error screen. Only `buildError` receives the error, but that is too late as `redirect` will redirect back the user before that. This can be easily triggered by deep links sadly.

To reproduce please run my attached code sample. As `onException` and `errorBuilder` are mutually exclusive, one of them is commented out.

I'm using `go_router`: `14.4.1` with flutter `3.24.4` on Android (but I don't think it is platform dependant, however I don't know and care about web or any non-mobile).

### Expected results

The `GoRouterState`'s `error` property should be non-null when the matched location is not registered among the possible routes. In the example code all errors should be like the following after tapping the `Open a non-existing route` button:

> /404: GoException: no routes for lcoation: /404

### Actual results

The `GoRouterState`'s `error` property is `null` when opening a non-existing route. In the example app all errors are like this:

> /404: No error

### Code sample

<details open><summary>Code sample</summary>

```dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() => runApp(const MyApp());

final _errorNotifier = ValueNotifier<String>('');
final _redirectErrorNotififer = ValueNotifier<String>('');

extension on GoRouterState {
  String get errorMsg => '$matchedLocation: ${error ?? 'No error'}';
}

final _router = GoRouter(
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
  ],
  redirect: (context, state) {
    _redirectErrorNotififer.value = state.errorMsg;
    return null;
  },
  onException: (context, state, router) =>
      _errorNotifier.value = state.errorMsg,
  // errorBuilder: (context, state) => HomeScreen(error: state.errorMsg),
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

class HomeScreen extends StatefulWidget {
  final String? error;

  const HomeScreen({super.key, this.error});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String? _goRouterOnExceptionError;
  String? _goRouterRedirectError;
  late final _commonErrorLabel;

  @override
  void initState() {
    super.initState();
    _goRouterOnExceptionError = widget.error;
    _commonErrorLabel = widget.error != null ? 'buildError' : 'onException';
    _errorNotifier.addListener(_onExceptionErrorListener);
    _redirectErrorNotififer.addListener(_redirectErrorListener);
  }

  @override
  void dispose() {
    _errorNotifier.removeListener(_onExceptionErrorListener);
    _redirectErrorNotififer.removeListener(_redirectErrorListener);
    super.dispose();
  }

  void _onExceptionErrorListener() {
    setState(() {
      _goRouterOnExceptionError = _errorNotifier.value;
    });
  }

  void _redirectErrorListener() {
    setState(() {
      _goRouterRedirectError = _redirectErrorNotififer.value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home Screen')),
      body: SizedBox.expand(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ..._errorMessage(
                context, _commonErrorLabel, _goRouterOnExceptionError),
            ..._errorMessage(context, 'redirect', _goRouterRedirectError),
            ElevatedButton(
              onPressed: () => context.push('/404'),
              child: const Text('Open a non-existing route'),
            ),
          ],
        ),
      ),
    );
  }

  List<Widget> _errorMessage(
    BuildContext context,
    String label,
    String? message,
  ) {
    if (message == null) {
      return const <Widget>[];
    }
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    return [
      Text(label, style: theme.textTheme.labelLarge),
      ColoredBox(
        color: colorScheme.error,
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text(
            message,
            style: TextStyle(color: colorScheme.onError),
          ),
        ),
      ),
      const SizedBox(height: 15.0),
    ];
  }
}
```

</details>


### Screenshots or Video

_No response_

### Logs

_No response_

### Flutter Doctor output

<details open><summary>Doctor output</summary>

```console
[✓] Flutter (Channel stable, 3.24.4, on macOS 15.1 24B83 darwin-arm64, locale en-HU)
    • Flutter version 3.24.4 on channel stable at /Users/macbookpro/devtools/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 603104015d (3 weeks ago), 2024-10-24 08:01:25 -0700
    • Engine revision db49896cf2
    • Dart version 3.5.4
    • DevTools version 2.37.3

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/macbookpro/devtools/android_sdk
    • Platform android-34, build-tools 34.0.0
    • ANDROID_SDK_ROOT = /Users/macbookpro/devtools/android_sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16B40
    • CocoaPods version 1.16.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)

[✓] VS Code (version 1.95.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.100.0

[✓] Connected device (4 available)
    • SM S906U1 (mobile)              • RFCT20EQM0M           • android-arm64  • Android 14 (API 34)
    • macOS (desktop)                 • macos                 • darwin-arm64   • macOS 15.1 24B83 darwin-arm64
    • Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin         • macOS 15.1 24B83 darwin-arm64
    • Chrome (web)                    • chrome                • web-javascript • Google Chrome 130.0.6723.117
    ! Error: Browsing on the local area network for László’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)

[✓] Network resources
    • All expected network resources are available.

• No issues found!
```

</details>