# Troubleshooting

## Installing and running `smf`

### `smf: command not found`

`dart pub global activate` installs `smf` in the directory of globally activated Dart packages, `~/.pub-cache/bin` on macOS and Linux, which may not be on your `PATH`. Add it as the [Dart documentation](https://dart.dev/tools/pub/cmd/pub-global#running-a-script-from-your-path) describes, and open a new terminal.

### The Flutter SDK is missing or too old

`smf create` checks the Flutter SDK before it generates anything. It uses the `flutter` on the `PATH` and the `dart` of the same SDK. If there is no `flutter` there, [install Flutter](https://docs.flutter.dev/get-started/install) and add its `bin` directory to the `PATH`.

The apps need Flutter 3.44 or newer and Dart 3.12 or newer. `flutter_core` sets these constraints, and every app needs it for the app entry, so with an older SDK `smf create` stops before it generates anything and names both versions:

```text
Generation stopped because of 2 errors.
  error [flutter_core]: flutter_core needs Dart ^3.12.0, but the Flutter SDK at /path/to/flutter/bin/flutter has Dart 3.10.1. (Upgrade Flutter.)
  error [flutter_core]: flutter_core needs Flutter >=3.44.0, but the Flutter SDK at /path/to/flutter/bin/flutter has Flutter 3.41.0. (Upgrade Flutter.)
```

Upgrade Flutter, or put a newer Flutter SDK first on the `PATH`.

## Errors of `smf create`

The [exit code](https://doc.saymyframe.com/guides/smf-create.md#exit-codes) tells what kind of problem it was.

| Code | What to do |
| --- | --- |
| 64 | The command line was wrong or incomplete. The message says what to change, and `smf create --help` lists the options. A run without a terminal also stops with 64 when it would have to ask, such as for the app name, for what to do with an existing directory (`--on-conflict`), or for which of several modules provides a role. |
| 1 | Generation failed, and the messages say why. If the app was generated already, it stays in a temporary directory that the message names, so you can look at it. |
| 70 | An unexpected error, which is a bug of `smf` or of a module. Run the same command again with `--verbose` for the full log, and please [report it](https://github.com/saymyframe/smf_flutter_cli/issues). |

### "Leaving out …"

By default, `smf create` leaves out a module that cannot work in the app, with a warning that says why, and generates the rest. Add `--strict` to stop instead. A module that every app needs, such as `flutter_core`, is never left out: its problem stops the run. See [lenient and strict](https://doc.saymyframe.com/guides/smf-create.md#lenient-and-strict).

### "Generation stopped because of a warning about the app"

With `--strict`, a warning about what the app is made of stops the run too, such as `router.no_start` for an app whose routes mark none as a start screen. The message names the code of each warning, and the role that reported it. Fix what the warning names, such as `--start` for the start screen, or run without `--strict` to generate the app as it is.

### The app's directory already exists

In a terminal, `smf create` asks what to do. Without one, choose with `--on-conflict`: `replace`, `copy` to create the app next to it, or `cancel`.

## Building the app

### A release build has the id and the name of `dev`

A `flutter` command without `--flavor` builds `dev`, in release mode too. A build for a store names the flavor, as in `flutter build appbundle --flavor prod`. See [Flavors](https://doc.saymyframe.com/guides/flavors.md#run-and-build-a-flavor).

### The APK or the app bundle is not where a script expects it

The name of the flavor is part of the path: `build/app/outputs/flutter-apk/app-dev-release.apk` and `build/app/outputs/bundle/devRelease/app-dev-release.aab`, also for a build without `--flavor`.

### iOS builds with Xcode 27

Xcode 27 builds only for iOS 15 and newer, which Flutter follows from version 3.47. With Flutter 3.44 and Xcode 27:

- `flutter build ios --simulator` fails in `debug_unpack_ios`: Flutter passes both architectures of the simulator to `lipo -verify_arch`, which takes one in Xcode 27. Build for the architecture of your Mac:

  ```bash
  FLUTTER_XCODE_ARCHS=arm64 flutter build ios --simulator
  ```

- A plugin that installs with CocoaPods and asks for iOS 13 or 14 keeps that version, which Xcode 27 refuses. Flutter 3.47 raises it to the version of the app.

So for iOS builds with Xcode 27, use Flutter 3.47 or newer. The apps of SMF set the minimum iOS version to 15.0.

### Xcode cannot sign the app for an iPhone with a free personal team

The iOS app signs with `ios/Runner/Runner.entitlements`. Push messages add the entitlement `aps-environment` there, and Sign in with Apple adds `com.apple.developer.applesignin`. Signing with either needs a team of the paid Apple Developer Program. The simulator signs the app without one. Signing the app once with such a team is a step by hand of the app, and the [click-by-click guide](https://doc.saymyframe.com/setup/app-entry-ios-app-id.md) shows each screen.

To run the app on an iPhone with a free personal team, remove the key from `ios/Runner/Runner.entitlements`. Without `aps-environment` the app runs without an address for push messages, and without `com.apple.developer.applesignin` Sign in with Apple fails with `notConfigured`. See [push messages](https://doc.saymyframe.com/guides/notifications.md#the-address) and [`apple_sign_in`](https://doc.saymyframe.com/modules/apple-sign-in.md#the-capability).

### `flutter analyze` reports problems in `build/`

On macOS, Flutter copies the Swift packages of plugins that depend on other plugins into `build/ios/SourcePackages`, together with their examples, which do not analyze in the app. That is why the `analysis_options.yaml` of a generated app leaves `build/**` out of the analysis. Keep that exclusion if you replace the file.

### `flutter analyze` fails in a directory whose path has letters beyond ASCII

With Flutter 3.44 and 3.47, `flutter analyze` stops with exit code 255 and a `FormatException: Unterminated string` from `LspByteStreamServerChannel` when the path of the app has a letter beyond ASCII, such as `é` or a Cyrillic letter. An app of `flutter create` in that directory fails the same way, so the cause is in Flutter, not in SMF: the tool counts the length of a message to the analysis server in UTF-16 code units instead of bytes. Flutter fixed it in its main branch ([flutter/flutter#191377](https://github.com/flutter/flutter/pull/191377)).

Until your Flutter has the fix, keep the app in a directory whose path has only ASCII letters, or analyze it with `dart analyze`, which works there and reports the same issues:

```bash
dart analyze --fatal-infos
```

`flutter pub get` and `flutter test` work in such a directory.

## CI

### `tool/write_env.dart` stops

`.env has values, which this script would replace` means what it says: the script never replaces a value, so delete `.env` to write it again. When it names a key, the value of that key has a double quote, a control character or a line separator, which `.env` cannot hold. See [Values for each environment](https://doc.saymyframe.com/guides/ci.md#values-for-each-environment).

### CI turned red with no change in the code

CI runs the latest stable Flutter, so a new release can fail `flutter analyze` on a new deprecation, and the format check on a change of the formatter. See [The version of Flutter](https://doc.saymyframe.com/guides/ci.md#the-version-of-flutter).

## Testing the app

### A test that runs `bootstrap()` fails with "The SharedPreferencesAsyncPlatform instance must be set"

The app has [`shared_preferences`](https://doc.saymyframe.com/modules/shared-preferences.md), and `bootstrap()` opens the preferences, which a test has no platform side for. Put the preferences of the package into memory before the test runs `bootstrap()` or `main()`:

```dart
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';

// ... and in the test, before bootstrap() or main():
SharedPreferencesAsyncPlatform.instance =
    InMemorySharedPreferencesAsync.empty();
```

Both names come from the package `shared_preferences_platform_interface`, each from a library of its own. Add the package to the app as a dev dependency with `flutter pub add dev:shared_preferences_platform_interface`.

### A test that runs `bootstrap()` fails in the start of the notifications or of the push messages

The packages of [`local_notifications`](https://doc.saymyframe.com/modules/local-notifications.md#in-tests) and [`firebase_messaging`](https://doc.saymyframe.com/modules/firebase-messaging.md#in-tests) have no platform side for tests, and `bootstrap()` starts both. `flutter test` registers no implementation of the plugin of flutter_local_notifications for the platform. So a test that runs `bootstrap()` or `main()` calls `AndroidFlutterLocalNotificationsPlugin.registerWith()` first, and answers the method channels itself with `setMockMethodCallHandler` of `TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger`: `dexterous.com/flutter/local_notifications` for the notifications and `plugins.flutter.io/firebase_messaging` for the push messages. The guide for coding agents of the app says the same. A test of a screen needs none of this: it gives `appNotifications.start()` or `appPush.start()` a service of its own; see [Notifications and push messages](https://doc.saymyframe.com/guides/notifications.md#in-tests).

### A test that signs a user up before the app starts never finishes

A call of `appSession` that is made before the app starts waits for the start-up. Made in the body of a widget test, it waits in the fake time of that body, and a start-up that runs in real time, as in `tester.runAsync`, then never completes. Make the call outside the body of a test, as in a `setUpAll`, and do not await it. See [Sign-in](https://doc.saymyframe.com/guides/sign-in.md#in-tests).

## Texts and languages

### `context.l10n` lacks a text that an ARB file has

The app has [`gen_l10n`](https://doc.saymyframe.com/modules/gen-l10n.md), and gen-l10n of Flutter writes the code that reads the ARB files. Run `flutter gen-l10n` after every change of the files in `lib/l10n`, a new file included: `flutter pub get` generates the code in a fresh clone of the app, and does not notice a new file. The language also goes into `appLocales` and into the `Info.plist` of the iOS app; see [Add a language](https://doc.saymyframe.com/guides/languages.md#add-a-language).

## Sign-in

### Signing in or signing up fails with `notConfigured`

The app has [`firebase_auth`](https://doc.saymyframe.com/modules/firebase-auth.md), and the way to sign in is not enabled in the Firebase project. The screens of [`sign_in`](https://doc.saymyframe.com/modules/sign-in.md) then say only that sign-in is unavailable, and show the `developerHint` of the failure below that text in a debug build. The hint tells both ways to enable it:

```text
AuthFailure: notConfigured (Sign-in is not enabled in the Firebase project my-project (Firebase answered operation-not-allowed). To fix it, run `dart tool/enable_firebase_sign_in.dart` in the directory of the app, or enable Email/Password, and Anonymous for an app that signs in anonymous users, in the Firebase console under Authentication > Sign-in method: https://console.firebase.google.com/project/my-project/authentication/providers)
```

Run the script, or enable Email/Password in the Firebase console, and Anonymous for an app in the mode `anonymous`; see [Firebase](https://doc.saymyframe.com/guides/firebase.md#sign-in-methods-of-firebase-authentication). On iOS and macOS, Firebase may answer for a project without Authentication with an internal error that says no more. The hint then says that this may be the cause, or another internal error.

### The iOS app stops when the user taps Continue with Google

On iOS, the Google Sign-In SDK stops the app when the app has no iOS client of Google or not the URL scheme of that client in `ios/Runner/Info.plist`. google_sign_in does not catch the exception that the SDK throws then. With `firebase_auth`, run `dart tool/enable_firebase_sign_in.dart` after `tool/configure_firebase.dart`, which downloads the Firebase files with the client and writes the URL scheme; see [what Google needs](https://doc.saymyframe.com/modules/google-sign-in.md#what-google-needs).

### On Android, Continue with Google fails with `notConfigured`

Google knows the Android app by the SHA-1 of the key that signs it. When the Google project does not know the key, Credential Manager answers `[16] Account reauth failed.` after the user picked an account, and `googleCredential()` fails with `notConfigured`, with that answer in its `developerHint`. A device that cannot reach Google gets the same answer. Register the SHA-1 and SHA-256 of the key: the script of `firebase_auth` registers those of the debug key of the machine that runs it, and a release needs those of the upload key and of the app signing key of Google Play; see [Firebase](https://doc.saymyframe.com/guides/firebase.md#google).

`[28444] Developer console is not set up correctly.` means something else: Google does not take the server client of the app as a web client of the Google project, as when `initialize()` gets the id of the Android client; see [what Google needs](https://doc.saymyframe.com/modules/google-sign-in.md#what-google-needs).

### Deleting an account that signs in with Apple fails with `notConfigured`

Firebase revokes the tokens of Apple when such an account is deleted, with the Services ID and the key of Apple that you enter in the settings of the Apple provider in the Firebase console. Until they are set, the deletion fails with `notConfigured`, and the account stays. See [Firebase](https://doc.saymyframe.com/guides/firebase.md#apple).

### In the mode `anonymous`, the app starts without a user

The app signs in an anonymous user when it starts and waits for that for at most 3 seconds. When the sign-in fails or takes longer, as without a network or while anonymous users are not enabled at the provider, the app starts with a `SignedOutSession`. In debug mode it prints the error of a sign-in that failed:

```text
The anonymous sign-in failed, so the app has no user: AuthFailure: notConfigured (...)
```

The app tries again each time the user comes back to it. With `firebase_auth`, enable Anonymous in the Firebase project: run `dart tool/enable_firebase_sign_in.dart` again once `authMode` is `anonymous`; see [Firebase](https://doc.saymyframe.com/guides/firebase.md#sign-in-methods-of-firebase-authentication).

### Every screen is open although the mode is `required`

The mode keeps nobody from a screen on its own. A guard of the routes that reads `appSession.allowsApp` does, and an app without such a guard shows every screen in every mode. The feature [`sign_in`](https://doc.saymyframe.com/modules/sign-in.md) declares that guard. An app with `firebase_auth` and without `sign_in` has none; see [an app without the screens](https://doc.saymyframe.com/guides/sign-in.md#an-app-without-the-screens).

### `smf create` warns that a route is for users with an account

A module of the app asks for an account on a route, and no module of the app has a guard that stands for that condition, so every user gets to the screen. The app is generated all the same. Add `sign_in`, whose guard stands for the condition, or keep the other users away with a guard of your own that reads `appSession.hasAccount`; see [an app without the screens](https://doc.saymyframe.com/guides/sign-in.md#an-app-without-the-screens).

### In an app without tabs, nothing leads to the account

The entry of the account is on the settings screen, which a main navigation shows, such as the tabs of [`bottom_tabs`](https://doc.saymyframe.com/modules/bottom-tabs.md). In an app without a main navigation, no screen leads to the settings screen. Open it from a screen of yours with `context.nav.settings.settings().push<void>()`, or open the account directly with `context.nav.signIn.account().push<void>()`.

### The script says that it cannot tell the sign-in mode of the app

`tool/enable_firebase_sign_in.dart` reads the mode from the declaration of `authMode` in `lib/core/auth/app_session.dart`. Keep that declaration in the form in which it was generated, on a line that it starts and with a mode as its value:

```dart title="lib/core/auth/app_session.dart"
const AuthMode authMode = AuthMode.required;
```

A computed value, an indented declaration or a second declaration stops the script, which does not guess.

### The script says that `firebase.json` is missing or has no project

`tool/configure_firebase.dart` writes the Firebase project of each flavor into `firebase.json`, and the script reads it from there. Configure a flavor first, with the command of the section "Firebase" of the README of the app, or name the project:

```bash
dart tool/enable_firebase_sign_in.dart --project <id>
```

When `firebase.json` has several projects, as when flavors have projects of their own, the script stops and lists them. A run covers one project, so run it once for each.

### The Firebase CLI did not enable the sign-in methods

The script prints what to try after the Firebase CLI failed:

- It runs the Firebase CLI in a temporary directory, where an account that `firebase login:use` chose for the directory of the app does not apply. If the Firebase CLI ran as the wrong account, name the account: `dart tool/enable_firebase_sign_in.dart --account <email>`.
- The log of the Firebase CLI, `firebase-debug.log`, was in that directory, which the script removes. To see what the Firebase CLI did, run the script with `--debug`.
- The Firebase console enables the methods too, under Authentication > Sign-in method.

With a Firebase CLI older than 15.34.0, the script stops before that and tells how to update it.

## REST API

### A request fails with `notConfigured`

`REST_BASE_URL` in `.env` is empty, or is no address of a server, and the path is relative. Fill it in, give the command `--dart-define-from-file=.env`, and start `flutter run` again. See [The address](https://doc.saymyframe.com/guides/rest.md#the-address).

### A request in a test fails with `rejected`

`flutter test` answers 400 to every `HttpClient`. Start a `RestTestServer`, which takes that stand-in away. See [In tests](https://doc.saymyframe.com/guides/rest.md#in-tests).

### A test with a request never finishes, or fails with a pending timer

Inside `testWidgets` the time is fake, and a real socket never completes there. Make the request in `tester.runAsync`, and let it end before the body of the test does.

## Notifications and push messages

### The address for push messages stays `null`

The app works without an address, and `appPush.address` is `null` until the platform gives one. On iOS the address needs a token of the Apple Push Notification service, which iOS gives only to a build signed with the entitlement `aps-environment`. The iOS simulator on a Mac gets a token, and one on a macOS virtual machine, such as a hosted CI runner, never does. On Android, Firebase has no token without Google Play services or a network. See [the address](https://doc.saymyframe.com/guides/notifications.md#the-address) and [`firebase_messaging`](https://doc.saymyframe.com/modules/firebase-messaging.md#the-address-of-the-device).

## Firebase

### The app stops at start-up with an `UnsupportedError`

The flavor of the build is not configured for Firebase yet, and its `lib/firebase_options_<flavor>.dart` is still the placeholder. Run `dart tool/configure_firebase.dart <flavor> --project=<id>` in the app. See [One project for each flavor](https://doc.saymyframe.com/guides/firebase.md#one-project-for-each-flavor).

An error that says that the app has no Firebase options for the flavor `null` comes from a run of the scheme `Runner` in Xcode. Select the scheme of a flavor instead. See [Xcode](https://doc.saymyframe.com/guides/flavors.md#xcode).

### A build fails with `File google-services.json is missing`

Once one flavor is configured for Firebase, Android builds of the others need their files too. Run `dart tool/configure_firebase.dart <flavor> --project=<id>` for the flavor of the build. A plain `flutter build` builds `dev`, so that is the flavor to configure first. See [A flavor that is not configured](https://doc.saymyframe.com/guides/firebase.md#a-flavor-that-is-not-configured).

### An iOS build fails with `FirebaseJsonException: Please run "flutterfire configure"`

The build phase of Crashlytics looks up the build configuration in `firebase.json`, and the flavor of the build is not configured. Run `dart tool/configure_firebase.dart <flavor> --project=<id>` on a Mac, rather than `flutterfire configure`.

### `flutter run` or `flutter build ios` fails in the phase `flutterfire upload-crashlytics-symbols`

The app was configured with flutterfire_cli 1.4.0, whose phase for Crashlytics does not find the upload script where Flutter puts the Swift packages. Activate 1.4.1 and run `tool/configure_firebase.dart` again on a Mac for each configured flavor. See [Firebase](https://doc.saymyframe.com/guides/firebase.md#an-app-configured-with-flutterfire_cli-140).

### `flutter build ipa` fails in the phase for Crashlytics

The phase of flutterfire_cli 1.4.1 does not find the upload script when Flutter archives the app. `tool/configure_firebase.dart` fixes the phase after each of its runs, but `flutterfire configure` by hand writes the phase again. After such a run on macOS, run `dart tool/firebase_configured/fix_crashlytics_phase.dart`. See [Crashlytics and `flutter build ipa`](https://doc.saymyframe.com/guides/firebase.md#crashlytics-and-flutter-build-ipa).

### `firebase login` does not finish on a remote machine

`firebase login` waits for the browser to come back to a server on the machine where it runs, which a browser on another machine cannot reach. SMF runs `firebase login --no-localhost` instead when `SSH_CONNECTION`, `SSH_CLIENT` or `SSH_TTY` has a value, as in a session over SSH. If none of them has one, press Ctrl-C to stop the login, and log in yourself with `firebase login --no-localhost`. The run goes on without the login and prints the command to configure Firebase later.

### SMF says that the Firebase login has expired or is no longer valid

`firebase login:list` still lists your account, but Google rejected the login when SMF listed your Firebase projects with `firebase projects:list --debug`. A login can expire or be revoked, for example when you remove the Firebase CLI's access in your Google account or when your organization asks you to sign in again. Log in again with `firebase login --reauth`, or over SSH with `firebase login --reauth --no-localhost`. In a terminal, SMF offers to do this for you. Plain `firebase login` only says that you are logged in already. Until the login works, `flutterfire configure` finds no Firebase project and offers to create one.

### SMF says that the Firebase login could not be checked

SMF could not finish listing your Firebase projects with `firebase projects:list --debug`, so it does not know whether the login works, and it leaves the configuration for later. The warning gives the reason.

A line such as `request to … failed, reason: getaddrinfo ENOTFOUND …`, or one with `ECONNREFUSED`, `ETIMEDOUT` or a certificate error, means that the Firebase CLI could not reach Google. Check your internet connection. Behind a proxy, set `HTTPS_PROXY` in the terminal that runs `smf`; the Firebase CLI reads it.

`did not finish in 40 s` means that no answer came, as happens on a network that drops requests. SMF stops the command after 40 seconds and shows the progress "Checking the machine" while it waits.

Once the network works, run `smf create --explain` to see the state of every check, then configure Firebase in the app with the command from its README.

### The Firebase CLI is installed, but SMF does not find it

SMF looks for `firebase` on the `PATH` of the terminal that runs `smf`. After installing it yourself, open a new terminal, or add its directory to the `PATH`, and run `smf create --explain` to see the state of every check.

### SMF says that the Firebase CLI does not run

SMF runs `firebase --version` to check the Firebase CLI. A `firebase` command can be on the `PATH` and still fail, such as one that runs the Firebase CLI of another Node.js, or a Firebase CLI on a Node.js that is too old for it. The warning says how the command ended. In a terminal, SMF offers to install the Firebase CLI again; see [Firebase](https://doc.saymyframe.com/guides/firebase.md#before-generation-the-machine).

If the old command still does not run after that installation, SMF says so. A new terminal may find it before the new Firebase CLI, as with a Firebase CLI of an older default version of nvm, so remove it.
