class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val client = ChatClient.Builder(apiKey, this)
.withPlugins(
StreamOfflinePluginFactory(appContext = this),
StreamStatePluginFactory(config = StatePluginConfig(), appContext = this),
)
.build()
}
}Troubleshooting
Common issues and solutions when integrating the Stream Chat Android SDK.
Initialization Issues
ChatClient Not Initialized
Symptom: IllegalStateException: ChatClient is not initialized
Cause: Attempting to use ChatClient.instance() before calling ChatClient.Builder(...).build().
Solution: Initialize ChatClient in your Application class:
ProGuard/R8 Issues
Symptom: Crashes in release builds, missing classes, or serialization errors.
Cause: ProGuard/R8 obfuscation removing required classes.
Solution: The SDK includes consumer ProGuard rules automatically. See the consumer rules in the SDK repository for details. If you still encounter issues, ensure the consumer rules are being applied correctly by your build configuration.
Push Notification Issues
FCM Token Not Refreshing
Symptom: Push stops working after token refresh.
Solution: Use FirebaseMessagingDelegate to handle token registration and refresh:
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
FirebaseMessagingDelegate.registerFirebaseToken(
token = token,
providerName = "MyFirebaseProvider", // Must match dashboard config
)
}
override fun onMessageReceived(message: RemoteMessage) {
FirebaseMessagingDelegate.handleRemoteMessage(message)
}
}Alternatively, you can manually register the device using addDevice:
ChatClient.instance().addDevice(
Device(
token = token,
pushProvider = PushProvider.FIREBASE,
providerName = "MyFirebaseProvider",
)
).enqueue { /* Handle result */ }State & UI Issues
Messages Not Updating
Symptom: New messages don't appear or UI doesn't reflect changes.
Cause: The channel is not being watched. Messages only update in real-time for watched channels.
Solution: Ensure the channel is watched before expecting real-time updates:
// Watch the channel to receive real-time updates
ChatClient.instance().channel(channelType, channelId).watch().enqueue { result ->
when (result) {
is Result.Success -> { /* Channel is now watched */ }
is Result.Failure -> { /* Handle error */ }
}
}For Compose, use the provided ViewModels which handle watching automatically:
val viewModelFactory = MessagesViewModelFactory(
context = context,
channelId = cid,
)
val messageListViewModel = viewModel<MessageListViewModel>(factory = viewModelFactory)Channel List Not Refreshing
Symptom: Channel list shows stale data or doesn't update with new messages.
Cause: Event handling not configured or custom filter issues.
Solution:
- Verify you're using the SDK's ViewModel or observing state correctly
- Check your
ChatEventHandlerif using custom event handling (see Channels State and Filtering) - Ensure the
StatePluginis configured
Compose Recomposition Issues
Symptom: Compose UI updates excessively or not at all.
Solution: Use the SDK's state hoisting pattern:
// Let the ViewModel handle state
val messagesState by messageListViewModel.currentMessagesState.collectAsStateWithLifecycle()
MessageList(
currentState = messagesState,
// ...
)Avoid creating new objects in composition that would trigger recomposition.
Connection Issues
Frequent Disconnections
Symptom: WebSocket disconnects frequently, especially on mobile networks.
Cause: Network changes or aggressive keep-alive settings.
Solution: Handle connectivity changes:
ChatClient.instance().clientState.connectionState
.onEach { state ->
when (state) {
is ConnectionState.Connected -> { /* Online */ }
is ConnectionState.Connecting -> { /* Reconnecting */ }
is ConnectionState.Offline -> { /* Handle offline */ }
}
}
.launchIn(scope)Token Expiry
Symptom: 401 Unauthorized errors or connection failures after some time.
Cause: JWT token expired and tokenProvider not configured.
Solution: Provide a token provider that fetches fresh tokens:
ChatClient.Builder(apiKey, context)
.withPlugins(/* ... */)
.build()
// Connect with token provider
ChatClient.instance().connectUser(
user = user,
tokenProvider = {
// Fetch fresh token from your backend
myBackend.getStreamToken(userId)
}
).enqueue { result ->
// Handle result
}For more details on handling token expiry with push notifications, see the Push Notifications documentation.
Memory & Performance
Message List Performance
Symptom: Laggy scrolling in channels with many messages.
Solution:
- Use
MessageLimitConfigto limit in-memory messages - For Compose, the
MessageListusesLazyColumnwhich virtualizes automatically - For XML Views, ensure you're not blocking the main thread in custom view holders
Debugging Tips
Enable Logging
Enable detailed logging to diagnose issues:
ChatClient.Builder(apiKey, context)
.logLevel(ChatLogLevel.ALL)
.loggerHandler(AndroidStreamLogger())
.build()Filter logcat by tag Chat: to see SDK logs.
Inspect Network Requests
Use the ApiRequestsAnalyser to track API calls:
val analyser = ApiRequestsAnalyser.get()
analyser.dumpRequestByName("QueryChannels")Check Connection State
Monitor connection state for debugging:
ChatClient.instance().clientState.connectionState.value
ChatClient.instance().clientState.user.value
ChatClient.instance().clientState.initializationState.value