Troubleshooting

There are several possible integration issues that can lead to calls not being established. This section will cover the most frequent ones.

Connection issues

Connection issues usually happen when you provide an invalid token during the SDK setup. When this happens, a web socket connection can't be established with our backend, resulting in errors when trying to connect to a call.

  • Ensure the token is valid and generated for the correct user.
  • Verify that the token matches the user ID specified during the initialization of the StreamVideo instance.

During development, hardcoded tokens often lead to mismatches. Always ensure the token corresponds to the user currently being initialized.

Expired tokens

When you initialize the StreamVideo object, you provide a token, as described here. The tokens generated in the docs have an expiry date, therefore please make sure to always use a token with a valid expiry date. You can check the contents of a JWT token on websites like this one.

Additionally, when expiring tokens are used, you need to provide a tokenLoader when creating StreamVideo, that will be invoked when the existing token expires. This is your chance to update the token by calling your backend.

Wrong secret for token generation

When you start integrating the SDK into your app, you might copy-paste the token from the docs into your project. However, that will not work. Tokens are generated with the help of the app secret (available in your dashboard), and are unique per app id. Your app id is different than the demo apps we have as examples in our docs.

On website like this one, you can verify if the token is signed with the correct signature.

While developing, you can manually generate tokens by providing your secret and the user's ID here. However, note that for production usage, your backend would need to generate these tokens.

Third-party network debuggers

There are network debuggers like Wormholy, that allow you to see all the network requests done with your app. However, some of them can interfere and block our web socket connection, like in this case. In order to prevent this, you need to exclude our hosts from debugger tools, as described on the linked issue.

Members in a call

One common issue is that you only specify one user and try to call the same user on another device. This will not work, if you are the caller, you will not receive a notification that you're being called - you can't call yourself.

As you would do it in the real world, you would need to specify another member (or members) that you want to call. Another important note - that member should also exist in Stream's platform (it must have connected at least once). This is needed because we need to know the user's device and where to send the call notification.

Reusing a call id

Call IDs in general can be reused - you can join a call with the same id many times. However, the ringing is done only once per call ID. Therefore, if you implement calls with ringing, make sure that you provide a unique ID every time, in order for the ring functionality to work. One option is to use a UUID as a call ID.

(iOS) CallKit integration issues

If you followed the CallKit guide, and still have issues, here are some troubleshooting steps:

  • make sure there are no connection issues (see points above)
  • make sure you don't have a Do Not Disturb focus turned on (or any other focus that might block CallKit)
  • check if the generated VoIP certificate matches the bundle id specified in the dashboard
  • check if the app is using the correct bundle id that also corresponds to the bundle id set for APN provider
  • make sure you selected Apple Push Notification service SSL (Sandbox & Production) instead of Apple Push Notification service SSL (Sandbox) when creating a Push notification service on developer.apple.com
  • check if you have created push providers and you specified their correct names when creating the SDK
  • check the "Webhook & Push Logs" section on the dashboard to see if there are any push notification failures
  • make sure the app has Push Notifications Capability added in Xcode
  • check that IPHONEOS_DEPLOYMENT_TARGET is set to 14.0 or higher in project.pbxproj for all build configurations (Debug, Release, Profile). New Flutter projects default to 13.0, which will cause a build error because stream_video_push_notification requires iOS 14.0+.
  • check that .entitlements files exist (e.g. Runner.entitlements and RunnerDebug.entitlements) with the aps-environment key, and that CODE_SIGN_ENTITLEMENTS is set in the Runner build configurations in project.pbxproj. Without these, iOS will silently refuse to deliver VoIP and regular push notifications. New Flutter projects do not include entitlements files by default — you can add them manually or via Xcode's Signing & Capabilities tab (+ Capability → Push Notifications).
  • check if correct background modes are set in Xcode (processing, remote-notification, voip)
  • try sending a hardcoded VoIP notification using a third-party service, to make sure your app integration is correct

Note that if you have failed to report a VoIP notification to CallKit, the operating system may stop sending you notifications. In those cases, you need to re-install the app and try again.

Device tokens out of sync

While debugging, the device tokens registered on the backend can get out of sync with the actual device. For example:

  • Stale tokens — Old tokens from previous installs or devices may still be registered, so the backend might target the wrong or invalid token.
  • Local cache — The SDK may cache the token and assume it is already registered, so the client does not re-send it and the backend never receives the current token.

If push or ringing notifications stop working and connection/token/CallKit setup looks correct, it helps to inspect registered tokens, clear them, and re-register the current device.

In your app you can:

  1. List registered devices — Call StreamVideo.instance.getDevices() to see which tokens are currently registered for the user.
  2. Clear all tokens — For each device, call StreamVideo.instance.removeDevice(pushToken: device.pushToken) to remove it from the backend.
  3. Re-register this device — Use the push notification manager to unregister and then register again (e.g. unregisterDevice() then registerDevice()), so the current device token is sent to the backend again.

Exposing this in the UI (e.g. a “View registered device tokens” screen with a “Clear all tokens and re-register this device” button) makes it easy to reset token state during development and verify that the current device is the only one registered after the action.

Missed call notifications not received

If missed call notifications are not being delivered despite push providers and device tokens being correctly registered, check your Stream Dashboard configuration:

  1. Navigate to your call type settings → Notification templates.
  2. Verify that the Call Missed template is enabled (toggled on). By default it may be disabled.
  3. Verify that the template body is not empty. If the template content is blank, the backend will silently skip sending the notification even when the template is enabled.

You can confirm whether notifications are actually being sent by checking the Webhook & Push Logs section in the Dashboard. If no push log entry appears for the missed call event, the issue is on the dashboard configuration side rather than the device/client side.

(iOS) Duplicate notifications when using both Chat and Video SDKs

When an app integrates both the Stream Chat and Stream Video SDKs, a common setup is to use Firebase (FCM) for chat push notifications and APN/VoIP push for video ringing (CallKit) on iOS. In this configuration, Firebase also delivers video ringing notifications (call.ring, call.missed) through its own channel — in addition to the APN/VoIP delivery that already triggers CallKit. If the foreground FCM listener forwards these to handleRingingFlowNotifications(), users will experience duplicate notifications (double ring or extra missed-call banner).

Solution: In your foreground FCM handler, check whether the message is a video notification on iOS and skip it:

FirebaseMessaging.onMessage.listen((message) {
  if (isChatNotification(message)) {
    handleChatNotification(message, chatClient);
  } else if (CurrentPlatform.isIos && isVideoNotification(message)) {
    // On iOS video ringing is handled via APN/VoIP — ignore the
    // duplicate Firebase delivery to avoid double notifications.
    return;
  } else {
    StreamVideo.instance.handleRingingFlowNotifications(message.data);
  }
});

bool isChatNotification(RemoteMessage message) =>
    message.data['type'] == 'message.new';

bool isVideoNotification(RemoteMessage message) =>
    message.data['sender'] == 'stream.video';

On Android, video ringing relies entirely on FCM so these notifications must still be forwarded. The CurrentPlatform.isIos guard ensures only iOS skips the Firebase delivery. For the full setup, see the Android Firebase integration guide.

Logs

For further debugging, you can turn on more detailed logging. In order to do that, specify a logPriority when creating StreamVideo` instance.

StreamVideo(
    ...,
    options: const StreamVideoOptions(
      logPriority: Priority.verbose,
    ),
  );