When invoking SendbirdCall.connectWebSocket(), it throws error "already reconnecting"

[Problem/Question]

I copied the Sendbird Calls context and provider code on Sendbird Calls javascript quick start:

When I make connection using SendbirdCall.connectWebSocket, it says "Failed: WebSocket is closed before the connection is established.
" and my logger tells me “Error: websocket is reconnecting already”.

But, I’m not calling SendbirdCall.connectWebSocket in any other place.

So, I downgraded Sendbird Calls package from “^1.10.9” to “1.9.3”, now it’s working fine.


[SDK Version]

“^1.10.9”

[Reproduction Steps]

My provider code is as following:

import React, {
  ReactElement,
  useCallback,
  useEffect,
  useMemo,
  useReducer,
} from "react";
import type { AuthOption, DirectCall } from "sendbird-calls";
import SendbirdCall, {
  LoggerLevel,
  sdkVersion,
  SoundType,
  RoomType,
} from "sendbird-calls";

import CallContext, { initialContext } from "./context";
import type { ContextType } from "./context";
import { reducer } from "./reducer";
import { initialState } from "./state";
import { statefyDirectCall, statefyRoom } from "./statefy";

console.log({ SoundType, sdkVersion });

/**
 * Provider
 * ```tsx
 * <SbCallsProvider>
 *   <MyApp />
 * </Auth0Provider>
 * ```
 *
 * Provides the SbCallsProvider to its child components.
 */
const SbCallsProvider = ({
  appId,
  children,
}: {
  appId: string;
  children: ReactElement;
}): JSX.Element => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { calls } = state;
  const currentCall = useMemo(
    () => calls.find((call) => !call.isEnded),
    [calls]
  );
  const isBusy = useMemo(() => calls.some((call) => !call.isEnded), [calls]);

  const init = useCallback<ContextType["init"]>((nAppId) => {
    const listenerId = "device-change-listener";
    try {
      SendbirdCall.removeListener(listenerId);
    } catch (error) {}
    SendbirdCall.init(nAppId);
    SendbirdCall.setLoggerLevel(LoggerLevel.ERROR);
    SendbirdCall.addListener(listenerId, {
      onRinging: (call: DirectCall) => {},
      onAudioInputDeviceChanged: (current, available) => {
        dispatch({
          type: "UPDATE_AUDIO_INPUT_DEVICE_INFO",
          payload: { current, available },
        });
      },
      onAudioOutputDeviceChanged: (current, available) => {
        dispatch({
          type: "UPDATE_AUDIO_OUTPUT_DEVICE_INFO",
          payload: { current, available },
        });
      },
      onVideoInputDeviceChanged: (current, available) => {
        dispatch({
          type: "UPDATE_VIDEO_INPUT_DEVICE_INFO",
          payload: { current, available },
        });
      },
    });
    SendbirdCall.updateMediaDevices({ audio: true, video: true });
  }, []);

  useEffect(() => {
    if (appId) init(appId);
  }, [appId]);

  const ringingListenerId = "sb-call-listener";
  const auth = useCallback(async (authOption: AuthOption) => {
    const user = await SendbirdCall.authenticate(authOption);
    SendbirdCall.addListener(ringingListenerId, {
      onRinging: (call: DirectCall) => {
        dispatch({
          type: "RINGING",
          payload: statefyDirectCall(call, dispatch),
        });
      },
      onAudioInputDeviceChanged: (current, available) => {},
      onAudioOutputDeviceChanged: (current, available) => {},
      onVideoInputDeviceChanged: (current, available) => {},
    });

    await SendbirdCall.connectWebSocket();

    dispatch({ type: "AUTH", payload: user });
    return user;
  }, []);

  const deauth = useCallback<ContextType["deauth"]>(() => {
    SendbirdCall.removeListener(ringingListenerId);
    SendbirdCall.deauthenticate();
    dispatch({ type: "DEAUTH" });
  }, []);

  /*
    Media Device Control
   */
  const updateMediaDevices = useCallback<ContextType["updateMediaDevices"]>(
    (constraints) => {
      SendbirdCall.updateMediaDevices(constraints);
    },
    []
  );

  const selectAudioInputDevice = useCallback<
    ContextType["selectAudioInputDevice"]
  >((mediaInfo: InputDeviceInfo) => {
    SendbirdCall.selectAudioInputDevice(mediaInfo);
    dispatch({
      type: "UPDATE_AUDIO_INPUT_DEVICE_INFO",
      payload: { current: mediaInfo },
    });
  }, []);

  const selectAudioOutputDevice = useCallback<
    ContextType["selectAudioOutputDevice"]
  >((mediaInfo: MediaDeviceInfo) => {
    SendbirdCall.selectAudioOutputDevice(mediaInfo);
    dispatch({
      type: "UPDATE_AUDIO_OUTPUT_DEVICE_INFO",
      payload: { current: mediaInfo },
    });
  }, []);

  const selectVideoInputDevice = useCallback<
    ContextType["selectVideoInputDevice"]
  >((mediaInfo: InputDeviceInfo) => {
    SendbirdCall.selectVideoInputDevice(mediaInfo);
    dispatch({
      type: "UPDATE_VIDEO_INPUT_DEVICE_INFO",
      payload: { current: mediaInfo },
    });
  }, []);

  /*
    Direct Calls
   */
  const dial = useCallback<ContextType["dial"]>(
    (params) =>
      new Promise((res, rej) => {
        // TODO: this is really confusing...
        SendbirdCall.dial(params, (call, error) => {
          const statefulCall = statefyDirectCall(call as DirectCall, dispatch);
          if (error) {
            rej(error);
            return;
          }
          dispatch({ type: "ADD_CALL", payload: statefulCall });
          res(statefulCall);
        });
      }),
    []
  );

  const clearCalls = useCallback(() => {
    dispatch({ type: "CLEAR_CALLS" });
  }, []);

  /*
    Rooms
   */
  const createRoom = useCallback<ContextType["createRoom"]>(async (options) => {
    const room = await SendbirdCall.createRoom(options);
    const statefulRoom = statefyRoom(room, dispatch);
    dispatch({ type: "ADD_ROOM", payload: statefulRoom });
    return statefulRoom;
  }, []);

  const getCachedRoomById = useCallback<ContextType["getCachedRoomById"]>(
    (roomId) => {
      return state.rooms.find((x) => x.roomId === roomId);
    },
    [state.rooms]
  );

  const fetchRoomById = useCallback<ContextType["fetchRoomById"]>(
    async (roomId) => {
      const room = await SendbirdCall.fetchRoomById(roomId);
      const statefulRoom = statefyRoom(room, dispatch);
      if (state.rooms.find((x) => x.roomId === room.roomId)) {
        dispatch({ type: "UPDATE_ROOM", payload: statefulRoom });
      } else {
        dispatch({ type: "ADD_ROOM", payload: statefulRoom });
      }
      return statefulRoom;
    },
    [state.rooms]
  );
  const callContext: ContextType = {
    ...initialContext,
    ...state,
    init,
    auth,
    deauth,
    isAuthenticated: !!state.user,

    // Media Device Control
    updateMediaDevices,
    selectAudioInputDevice,
    selectAudioOutputDevice,
    selectVideoInputDevice,

    // Direct Calls
    currentCall,
    isBusy,
    dial,
    addDirectCallSound: SendbirdCall.addDirectCallSound,
    clearCalls,

    // Rooms
    createRoom,
    getCachedRoomById,
    fetchRoomById,
    RoomType,
  };

  console.log("call context", callContext.calls, callContext.calls.length);
  return (
    <CallContext.Provider value={callContext}>{children}</CallContext.Provider>
  );
};

export default SbCallsProvider;

// I call sendbirdCallsLogin function when I'm checking if user logged in or not in my AuthContext
 const sendbirdCallsLogin = async (
    userId: string,
    sendbirdAccessToken: string
  ) => {
    const appId = CALLS_APP_ID;
    const option: AuthOption = { userId };

    sbCalls.init(appId);
    // sbCalls.addDirectCallSound(SoundType.DIALING, "/sounds/Dialing.mp3");
    // sbCalls.addDirectCallSound(SoundType.RINGING, "/sounds/Ringing.mp3");
    // sbCalls.addDirectCallSound(
    //   SoundType.RECONNECTING,
    //   "/sounds/Reconnecting.mp3"
    // );
    // sbCalls.addDirectCallSound(
    //   SoundType.RECONNECTED,
    //   "/sounds/Reconnected.mp3"
    // );
    await sbCalls
      .auth(option)
      .then(async (user) => {
        storage.setItem("sbCalls", { appId, userId });
        console.log(`DEBUG: did i ever succeeded`);
      })
      .catch((error) => {
        console.log(`ERROR: - sendbirdCallsLogin is ${error}`);
      });
  };

[Frequency]
// How frequently is this issue occurring?
every time

[Current impact]
// How is this currently impacting your implementation?
significant

Hi, I have same issue. Have you solved this problem yet?

Hi! It seems that error doesn’t cause any problem so far.

I set the version to ^1.10.9

Try to see if the video call or call do get connected

Hello @syc,
Thanks for confirming that you were able to connected.

And @minhdat97 ,
You can try with our new JS SDK - v1.10.11. Let me know if you have any issues.