@@ -19,6 +19,7 @@ import '../api/backoff.dart';
1919import '../api/route/realm.dart' ;
2020import '../log.dart' ;
2121import '../notifications/receive.dart' ;
22+ import 'actions.dart' ;
2223import 'autocomplete.dart' ;
2324import 'database.dart' ;
2425import 'emoji.dart' ;
@@ -149,13 +150,31 @@ abstract class GlobalStore extends ChangeNotifier {
149150 /// and/or [perAccountSync] .
150151 Future <PerAccountStore > loadPerAccount (int accountId) async {
151152 assert (_accounts.containsKey (accountId));
152- final store = await doLoadPerAccount (accountId);
153+ PerAccountStore ? store;
154+ try {
155+ store = await doLoadPerAccount (accountId);
156+ } catch (e) {
157+ switch (e) {
158+ case ZulipApiException (code: 'INVALID_API_KEY' ):
159+ // The API key is invalid and the store can never be loaded
160+ // unless the user retries manually.
161+ final zulipLocalizations = GlobalLocalizations .zulipLocalizations;
162+ final account = getAccount (accountId)! ;
163+ reportErrorToUserModally (
164+ zulipLocalizations.errorCouldNotConnectTitle,
165+ details: zulipLocalizations.errorInvalidApiKeyMessage (
166+ account.realmUrl.toString ()));
167+ await logOutAccount (this , accountId);
168+ default :
169+ rethrow ;
170+ }
171+ }
153172 if (! _accounts.containsKey (accountId)) {
154- // [removeAccount] was called during [doLoadPerAccount].
155- store.dispose ();
173+ // [removeAccount] was called during or after [doLoadPerAccount].
174+ store? .dispose ();
156175 throw AccountNotFoundException ();
157176 }
158- return store;
177+ return store! ;
159178 }
160179
161180 /// Load per-account data for the given account, unconditionally.
@@ -912,7 +931,13 @@ class UpdateMachine {
912931 // at 1 kiB (at least on Android), and stack can be longer than that.
913932 assert (debugLog ('Stack:\n $s ' ));
914933 assert (debugLog ('Backing off, then will retry…' ));
915- // TODO tell user if initial-fetch errors persist, or look non-transient
934+ // TODO(#890): tell user if initial-fetch errors persist, or look non-transient
935+ switch (e) {
936+ case ZulipApiException (code: 'INVALID_API_KEY' ):
937+ // We cannot recover from this error through retrying.
938+ // Leave it to [GlobalStore.loadPerAccount].
939+ rethrow ;
940+ }
916941 await (backoffMachine ?? = BackoffMachine ()).wait ();
917942 assert (debugLog ('… Backoff wait complete, retrying initial fetch.' ));
918943 }
@@ -1044,7 +1069,13 @@ class UpdateMachine {
10441069 }
10451070 } catch (e) {
10461071 if (_disposed) return ;
1047- await _handlePollError (e);
1072+ try {
1073+ await _handlePollError (e);
1074+ } on AccountNotFoundException {
1075+ // Cannot recover by replacing the store because the account
1076+ // was logged out.
1077+ return ;
1078+ }
10481079 assert (_disposed);
10491080 return ;
10501081 }
0 commit comments