Skip to content

Runtime error if user logs out when reloading PerAccountStore #1354

@PIG208

Description

@PIG208

loadPerAccount has a check for whether the account exists or not at the end:

  Future<PerAccountStore> loadPerAccount(int accountId) async {
    assert(_accounts.containsKey(accountId));
    final store = await doLoadPerAccount(accountId);
    if (!_accounts.containsKey(accountId)) {
      // [removeAccount] was called during [doLoadPerAccount].
      store.dispose();
      throw AccountNotFoundException();
    }
    return store;
  }

Hypothetically, this check can only be reached if the account is logged out while loading the PerAccountStore. However, because of a runtime error that would happen right after the inner request returns, we cannot reach the if-block here.

The error comes from a null-check at the start of PerAccountStore.fromInitialSnapshot:

    final account = globalStore.getAccount(accountId)!;
[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: Null check operator used on a null value
#0      new PerAccountStore.fromInitialSnapshot (package:zulip/model/store.dart:285:54)
#1      UpdateMachine.load (package:zulip/model/store.dart:913:35)
<asynchronous suspension>
#2      LiveGlobalStore.doLoadPerAccount (package:zulip/model/store.dart:836:27)
<asynchronous suspension>
#3      GlobalStore.loadPerAccount (package:zulip/model/store.dart:155:15)
<asynchronous suspension>
#4      GlobalStore._reloadPerAccount (package:zulip/model/store.dart:135:19)

PerAccountStore.fromInitialSnapshot is called near the end of LiveGlobalStore.doLoadPerAccount with no asynchronous gap, meaning that the null-check will always fail ahead of AccountNotFoundException being raised if the user logs out during the asynchronous gap from the /api/register-queue request or globalStore.updateAccount:

This is a bug that the if (!_accounts.containsKey(accountId)) check was meant to catch. The error should be thrown earlier, ideally every time before the account is accessed, similar to the if (mounted) checks and BuildContext.

Metadata

Metadata

Assignees

No one assigned

    Labels

    a-syncEvent queue; retry; local echo; races

    Type

    No type

    Projects

    Status

    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions