Session token is valid when used in iOS but not in Android?

[Problem/Question]
I am using the React Native UI Kit, and we have both an iOS application and an Android application. The implementation is working perfectly for iOS, but for some reason, Android throws one of two errors:

  1. SendbirdError: This session token is invalid.
  2. SendbirdError: There was a network error.

[UIKit Version]
3.1.1

[Reproduction Steps]
Below is our implementation. Note that useIssueSendbirdToken is calling our backend server, which issues a session token for the requesting user by making a request to the /v3/users/:user_id/token endpoint and returning it to the client.

const { connect } = useConnection();
  const [sendbirdConnected, setSendbirdConnected] = useState(false);
  const [sendbirdAccessToken, setSendbirdAccessToken] = useState<
    string | undefined
  >();
  const [sendbirdAccessTokenExpired, setSendbirdAccessTokenExpired] = useState<
    boolean | undefined
  >();

  const issueSendbirdToken = useIssueSendbirdToken(token => {
    setSendbirdAccessToken(token);
  });

  useEffect(() => {
    SecureStore.getItemAsync(SENDBIRD_SESSION_ACCESS_TOKEN_EXPIRATION)
      .then(expiration => {
        if (expiration) {
          // if we have an expiration, it means we've previously fetched and stored a sendbird access token
          // so we should see if it's expired
          const tokenIsExpired =
            DateTime.fromISO(expiration).diffNow().milliseconds < 0;
          logger.info(
            `Setting sendbirdAccessTokenExpired to ${tokenIsExpired}`
          );
          setSendbirdAccessTokenExpired(tokenIsExpired);
        } else {
          // otherwise, we have never fetched and stored a sendbird access token,
          // so get a brand new one from the server
          logger.info(
            `Fetching new Sendbird token because we didn't find an expiration`
          );
          issueSendbirdToken.mutate();
        }
      })
      .catch(e => {
        logger.error(stringifyError(e));
      });
  }, []);

  useEffect(() => {
    const fetchToken = async () => {
      if (sendbirdAccessTokenExpired) {
        logger.info(
          `Fetching new Sendbird token because the existing one is expired`
        );
        issueSendbirdToken.mutate();
      } else {
        logger.info(
          `Fetching Sendbird token from local storage because it is not yet expired`
        );
        const isEnrolled = await LocalAuthentication.isEnrolledAsync();
        if (isEnrolled) {
          const authResult = await LocalAuthentication.authenticateAsync({
            disableDeviceFallback: true,
            cancelLabel: 'Cancel'
          });
          if (!authResult.success) {
            await authTokenManger.logOut();
          }
        }

        const token = await SecureStore.getItemAsync(
          SENDBIRD_SESSION_ACCESS_TOKEN
        );
        if (token) {
          logger.info(
            `Successfully retrieved Sendbird token from local storage`
          );
          setSendbirdAccessToken(token);
        }
      }
    };

    if (sendbirdAccessTokenExpired !== undefined) {
      fetchToken().catch(e => {
        showErrorToast(
          `Oops! Something went wrong when trying to connect to your chat. Please try again later.`
        );
        logger.error(`Failed to fetch Sendbird token e=${JSON.stringify(e)}`);
      });
    }
  }, [sendbirdAccessTokenExpired]);

  useEffect(() => {
    if (sendbirdAccessToken) {
      console.log({ sendbirdAccessToken });
      logger.info(`Found Sendbird access token; connecting to Sendbird`);
      connect(props.userUuid, {
        nickname: props.userFullName,
        accessToken: sendbirdAccessToken
      })
        .then(() => {
          setSendbirdConnected(true);
        })
        .catch(e => {
          logger.unknownError('Unable to connect to Sendbird', e);
        });
    }
  }, [sendbirdAccessToken]);

[Frequency]
100% of the time on Android.

[Current impact]
Completely unusable on Android.

I ended up fixing this by forcing the client to fetch a new token. For some reason, the token that the Android client was using had not expired, but it was invalidated at some point in time. We’ll put in some more robust error handling here to fix.