Skip to main content
Version: 5.21.0

Troubleshooting

A guide to diagnosing and resolving typical issues during integration and operation of the edna Chat Center Android SDK.

tip

Before diagnosing, enable detailed logging: ChatLoggerConfig(logLevel = ChatLogLevel.VERBOSE). Logcat will then show initialization, authorization, message-send, and WebSocket events. Configuration and LogInterceptorLogger.


SDK does not initialize

Symptoms

  • The app crashes when calling init()
  • IllegalStateException when calling authorize(), send(), or other SDK methods without a prior init()

Checklist

  1. Is init() called in Application.onCreate()?

    The correct place for the constructor and init():

    class MyApp : Application() {
    lateinit var chatCenterUI: ChatCenterUI
    override fun onCreate() {
    super.onCreate()
    chatCenterUI = ChatCenterUI(applicationContext).apply {
    init(providerUid = "YOUR_PROVIDER_UID", config = chatConfig)
    }
    }
    }

    If the constructor is called from an Activity — the SDK throws IllegalArgumentException:

    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    ChatCenterUI(this) // Activity context — throws IllegalArgumentException
    }
    }
  2. Is applicationContext passed (not an Activity context)?

    • ChatCenterUI requires an Application-level context
    • When an Activity context is passed, the SDK throws IllegalArgumentException with the message "pass an Application-level Context"
  3. Is providerUid set correctly?

    • The value is provided by edna during integration
    • An empty string leads to IllegalArgumentException
  4. Protection against double initialization

    • A repeated init() call re-initializes the SDK with a cleanup of the current state and re-creation of the transport layer.
    • If you store the instance in Application via lateinit var, check initialization before re-assigning:
      if (!::chatCenterUI.isInitialized) {
      chatCenterUI = ChatCenterUI(applicationContext).apply { init(...) }
      }

How to verify

  • In Logcat (filter ELog, see Collecting logs) after a successful init(), records about SDK initialization and bringing up the network transport appear.
  • The next authorize() call does not throw IllegalStateException.

The chat does not load

Symptoms

  • getChatFragment() returns null
  • The chat screen shows the loading indicator indefinitely
  • Connection error

Checklist

  1. Is the user authorized? getChatFragment() returns non-null only after authorize().

    chatCenterUI.authorize(chatUser, chatAuth)
    val fragment = chatCenterUI.getChatFragment()
  2. Are the server URLs correct? cloudHost in the simplified constructor accepts a full URL with a protocol — without a path:

    val transport = ChatTransportConfig(cloudHost = "https://company.edna.io")

    The SDK passes the cloudHost value directly into Retrofit.baseUrl() and OkHttp Request.url() — without the protocol you get IllegalArgumentException: Expected URL scheme during initialization. A URL with a path (e.g., "https://company.edna.io/api") produces an incorrect final address. If you need a non-standard port or path, use the full form with explicit rest, webSocket, dataStore (ChatTransportConfig).

  3. Does the device have network access?

    • Open https://<your-cloud-host>/ in the device's browser — expect HTTP 200/301/404 (any response from the server, not "connection refused")
    • With a VPN, make sure traffic is routed correctly: the SDK uses WSS (port 443) and HTTPS
    • In VERBOSE logs, the SDK shows the final WebSocket and REST URLs after normalization
  4. Does ProGuard/R8 not obfuscate the SDK?

    warning

    Without -keep rules in a release build, the SDK is guaranteed to crash on the first config load or message receive (JsonSyntaxException, ClassNotFoundException). The full set of rules for the SDK and its transitive dependencies is in Installation → Obfuscation; the rules must be in the app's proguard-rules.pro.

How to verify

  • In Logcat after a successful authorize() and chat opening, there are no exceptions, and records about receiving messages via WebSocket are visible.
  • chatCenterUI.getChatFragment() returns a non-null Fragment.

Messages do not send

send() returns false, the message hangs in "sending" status or shows an error icon. In descending probability:

  1. send() threw IllegalStateException — the SDK is not initialized, call init() first.
  2. send() returned false — the user is not authorized: authorize() was not called or returned an error.
  3. The WebSocket is disconnected — subscribe to ChatCenterUIListener.networkErrorReceived(error); on disconnect, WARNING/ERROR records appear in Logcat.
  4. The file exceeds the server limitAttachmentSettings.content.maxSize (in megabytes). A typical value is 30; for on-premise it differs. See Files and media do not send.

On a successful send, the status goes through the chain SENDING → SENT → DELIVERED → READ. The full set of MessageStatus values (including ENQUEUED, FAILED) is in the canonical section.


Files and media do not send

Symptoms

  • The attach button does not open the file picker
  • When picking a file, an "file too large" or "file type not supported" error is shown
  • The file is picked, but the send fails with an error
  • The image preview does not load

Checklist

  1. Are media read permissions granted?

    • The SDK shows the system request dialog itself when trying to attach a file: for CAMERA and storage — via its own service permission-request screen; for RECORD_AUDIO — via the standard ActivityResultContracts.RequestPermission() (see the Voice messages section)
    • On "Don't ask again" refusal for CAMERA/storage, the SDK shows its own AlertDialog with a button to go to the app's system settings. For the microphone, the SDK shows only a Toast — the navigation to settings must be implemented in your own UI.
    • Runtime permissions and their behavior across Android versions — Android permissions
  2. Does the file size not exceed the server limit?

    • The limit arrives in AttachmentSettings.content.maxSize (in megabytes) from the server when the config is loaded
    • A typical value is 30 MB, but for on-premise installations it may differ. Check with your edna administrator
    • Pre-filter large files on the app side — the error status from the server arrives asynchronously
  3. Is the file MIME type allowed?

    • Allowed extensions arrive in AttachmentSettings.content.fileExtensions
    • The server configuration may forbid .exe, .dll, .apk, and other potentially dangerous types
    • Logcat shows an error specifying the rejected extension
  4. Is camera access available (if photo capture is used)?

    • The SDK requests the CAMERA permission via the system dialog when trying to open the camera from the chat
    • If the permission is revoked — the camera button will be unavailable
  5. Is the file passed through ContentResolver, not directly via File?

    • If your code works with java.io.File over a Scoped Storage content:// URI before passing the file to the SDK, the send will fail. Use ContentResolver or pass URIs to the SDK API without your own pipeline.

How to verify

  • A test file below maxSize with an allowed extension is sent and transitions to status SENT.
  • In Logcat, on a server refusal, the reason is visible: "File size exceeds limit", "Disallowed file type", or similar.

Voice messages do not record or play

Symptoms

  • The voice-message record button does not respond to a long-press
  • Recording starts, but on release, sending does not happen
  • A received voice message does not play — no sound or a player error
  • In Logcat — MediaRecorder start failed / IllegalStateException

Checklist

  1. Is the RECORD_AUDIO permission granted?

    • The SDK requests the permission via the standard ActivityResultContracts.RequestPermission() when tapping the microphone button in the chat
    • If the user refuses, the button visually remains, but recording does not start
    • Check Settings → Apps → <your app> → Permissions → Microphone = Allow
  2. Is the microphone not occupied by another app?

    • A concurrent call, VoIP session, or active voice-recorder app locks the microphone. Close the competing app and try again.
  3. Does the server's fileExtensions configuration allow the recording format?

    • The SDK records voice in .ogg (OPUS) on Android 10+ (API 29+) and in .3gp (AMR-WB) on Android 9 and below
    • If the server configuration forbids these extensions, voice send will not pass
    • Check with the edna administrator that ogg and 3gp are present in AttachmentSettings.content.fileExtensions
  4. Does the recording duration not exceed maxSize?

    • The recording file size obeys the same server limit AttachmentSettings.content.maxSize (in megabytes)
    • A long recording (several minutes) may exceed it — the server rejects the send post hoc
  5. Playback: volume and Do Not Disturb

    • The SDK plays voice messages on the media sound channel — adjust the media-channel volume, not the ringer
    • Do Not Disturb mode set to "Total silence" mutes playback. Check DND exceptions for media

How to verify

  • A long-press on the microphone button with RECORD_AUDIO granted starts recording and shows a signal-level indicator.
  • Releasing after a pause longer than 1 second sends the file; the message status transitions SENDING → SENT.
  • Playing a received voice message plays audio without errors in Logcat.

Push notifications do not arrive

Symptoms

  • Notifications are not shown when the app is in the background
  • Notifications of other SDKs work, but edna does not

Checklist

The checks go from cheap build-time to runtime — work through the list top-down.

  1. Is google-services.json in the right place?

    • The file must be in the root of the app module (next to build.gradle), not next to AndroidManifest.xml
    • The com.google.gms.google-services plugin is connected in build.gradle (app) — without it, the file is ignored
  2. Is the same Firebase project connected in the edna admin panel? In the edna Chat Center admin panel, on the "Notifications" tab of the Android channel, Sender ID and service-account.json are specified. Both values must belong to the same Firebase project whose google-services.json is in the app. If the fields in the admin panel are empty or specify a different project, the edna backend cannot send push, even if the SDK correctly registered the token (a test push from Firebase Console will still arrive). Verify the values: Connecting the Android mobile chat.

  3. Is FirebaseMessagingService registered in the manifest?

    <!-- Replace .YourFcmService with the full name of your service -->
    <service android:name=".YourFcmService"
    android:exported="false">
    <intent-filter>
    <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
    </service>
  4. Is the FCM token passed to the SDK?

    // In your FirebaseMessagingService:
    override fun onNewToken(token: String) {
    // setFCMToken — a static method in the ChatCenterUI companion object
    ChatCenterUI.setFCMToken(token, applicationContext)
    }
  5. Is handleFCMMessage() called?

    // In your FirebaseMessagingService:
    override fun onMessageReceived(message: com.google.firebase.messaging.RemoteMessage) {
    // The SDK filters push by the field origin == "threads"; if the push is not from edna,
    // the call is ignored (a corresponding record appears in VERBOSE logs)
    chatCenterUI.handleFCMMessage(message.data)
    }
  6. handleFCMMessage behavior relative to init() state

    • With the synchronous form of init(): the handleFCMMessage() call waits for the current initialization to complete and then processes the message normally. If init() was never called — handleFCMMessage() throws IllegalStateException.
    • With initAsync(): the call is safe, the push is placed into an internal queue and processed after initialization completes.
  7. Is origin=threads present in the payload? If the push reaches the device but handleFCMMessage does nothing — check the origin field in message.data. When sent from the edna admin panel, it is embedded by the server push template; when sent manually via Firebase Console / AppGallery Connect, origin=threads must be added manually to Custom data. Template description: Push notification template setup.

How to verify

  • In Logcat after onNewToken(...), a record about saving the FCM token is visible.
  • On a test push (via Firebase console or backend), onMessageReceived is called, and a new message appears in the UI/Logcat.

Push notifications do not arrive (HMS / Huawei)

On Huawei devices without Google Play Services, Huawei Push Kit (HMS) is used, not FCM. Symptoms and diagnostic steps differ from Google. Basic setup (dependencies, manifest, token handler) is in Push notification setup → HMS; below — integration diagnostics.

Symptoms

  • A build with HMS on a Huawei device does not show notifications, while the FCM build works on other phones.
  • In ChatLogLevel.VERBOSE logs, the HCM token is empty or not passed.

Checklist

  1. Is the HCM token passed via the static method?

    // setHCMToken — a static method in the ChatCenterUI companion object
    override fun onNewToken(token: String) {
    ChatCenterUI.setHCMToken(token, applicationContext)
    }
    warning

    Do not confuse setFCMToken and setHCMToken — these are different channels. The SDK stores the token by type, and an incorrect call silently does nothing: push will not arrive.

  2. Is agconnect-services.json in the root of the app module?

    • The file is required by the HMS Gradle plugin to register the Service-ID. Without it, HMS is not activated.
  3. Is the HMS service (a subclass of HmsMessageService) registered in the manifest with the intent filter com.huawei.push.action.MESSAGING_EVENT?

  4. Is the HMS RemoteMessage.data decoded as base64 → JSON → Bundle, not given to the SDK as-is? Unlike FCM, HMS delivers the payload in RemoteMessage.data as a base64 string with JSON inside. Before calling handlePushMessage(bundle), the string must be decoded and unpacked into a Bundle — otherwise the SDK sees "origin=threads" as an invalid payload and ignores the push. Decoder example: Push notification setup → HMS, Step 2.

    note

    The import is critical: both FCM and HMS have a RemoteMessage class, but they are different. A mixed-up import breaks the build or causes NoSuchMethodError at runtime.

  5. Does App_package in the admin panel match the module's applicationId? In the edna Chat Center, on the "Parameters" tab of the Huawei channel, App_package is specified — this is the applicationId from build.gradle(.kts). If the values diverge (a typical mistake after renaming the package or when using a separate Huawei build with applicationIdSuffix), the edna backend will not send push to your app. Verify the value: Connecting the Huawei mobile chat → Channel parameters.

  6. Are the Huawei App ID and App Secret in the admin panel up to date? After OAuth 2.0 keys are re-created in AppGallery Connect, old values in the admin panel stop working silently — with no explicit error in logs. Verify Huawei App ID and Huawei App Secret in the channel against the current OAuth 2.0 Client ID and Client secret from AppGallery Connect → Project settings → App information.

How to verify

  • In Logcat after onNewToken(...), a record about saving the HCM token appears.
  • On a test push via AppGallery Push Kit, the message reaches onMessageReceived and then the SDK's UI/Logcat.

The theme is not applied

Symptoms

  • Colors/fonts do not match the specified ones
  • Dark theme does not switch

Checklist

  1. Is the theme set before init()?

    // Recommended order: theme → init()
    chatCenterUI.theme = myTheme
    chatCenterUI.darkTheme = myDarkTheme
    chatCenterUI.init(providerUid, config)

    Setting the theme after init() is supported, but changes take effect on the next chat screen opening. If the changes are not visible — restart the chat.

  2. Is the customization done via ChatComponents? The only public way to configure the theme is to pass ChatComponents into the ChatTheme constructor. Colors, fonts, and images are set on the component properties:

    val components = ChatComponents(applicationContext).apply {
    chatMainComponent.chatBackgroundColor = R.color.my_bg
    // configuration of other components...
    }
    val myTheme = ChatTheme(components)
    chatCenterUI.theme = myTheme

    The configurable component fields are listed in Design system → Components.

  3. Is the dark theme set as a separate ChatComponents instance?

    • If darkTheme is not set, the SDK uses the theme value in both modes
    • The light and dark themes must use different ChatComponents instances — otherwise color changes in one mode overlap with the other

How to verify

  • Open the chat after chatCenterUI.theme = .... The colors/fonts visually match the ones passed.
  • VERBOSE-level logs contain records about theme application when ChatFragment is created.

WebSocket disconnects

Symptoms

  • Frequent reconnects
  • Messages arrive with a delay
  • In logs: ChatCenterUIListener.networkErrorReceived or WARNING/ERROR messages in the SDK logs

Checklist

  1. Are timeouts not too small? WSConfig accepts values in seconds, default is 30. If the network is unstable, timeouts should be increased, not decreased. Too short intervals (5/5/5) lead to constant disconnects. For unstable networks, it is reasonable to raise to 60/60/30:

    val wsConfig = WSConfig(connectionTimeout = 60, sendTimeout = 60, pingInterval = 30)
  2. Is keepWebSocketActive enabled when needed?

    • If you need the unread counter outside the chat screen — set keepWebSocketActive = true in ChatConfig
    • Otherwise the WebSocket closes when leaving the chat screen
    note

    The parameter is marked deprecated — the resulting behavior is controlled by the server configuration. See ChatConfig → Deprecated parameters.

  3. Is automatic reconnect enabled?

    • If the logs show frequent isolated disconnects, set WSConfig.isReconnectEnabled = true; the SDK will restore the connection itself with exponential backoff and a "Connecting..." title in the chat toolbar.
    • If reconnect is already enabled but the connection still periodically drops without recovery — increase maxReconnectAttempts or check the stability of the device's network.
    • A full behavior description and pending-message behavior is in Network config → Automatic reconnect.
  4. Is SSL pinning configured correctly?

    • The certificate specified in ChatNetworkConfig(sslPinning = SSLPinningConfig(...)) must match the server one
    • We recommend specifying at least 2 certificates (current + backup): on a rotation on the server side, the backup ensures uninterrupted operation for all clients
    warning

    An SSL pinning error makes communication with the server completely impossible and is not accompanied by an understandable UI message — only networkErrorReceived(error) in the listener. Before publishing a release build, test pinning separately.

How to verify

  • In ChatCenterUIListener.networkErrorReceived(error), events arrive less than once per minute on a stable network.

Crashes in release builds

Symptoms

  • Works in debug, crashes in release
  • ClassNotFoundException or NoSuchMethodException in the stack trace

Checklist

  1. Are ProGuard/R8 rules added?

    warning

    Without -keep rules, the release build crashes on the first config load or message receive. Check obfuscation first on any release crashes.

    -keep rules for the SDK and its transitive dependencies (Gson, Retrofit, OkHttp, Markwon, Jsoup) are collected in Installation → Obfuscation. Copy them into the app's proguard-rules.pro entirely.

  2. Is mapping.txt saved?

    • With R8 enabled, class names in the stack trace are obfuscated (a.b.c.d). Without mapping.txt from the same build, it is impossible to restore a readable stack trace.
    • The file is generated at app/build/outputs/mapping/release/mapping.txt on every release build.
  3. Does the release build reproduce the problem locally?

    • Build assembleRelease locally and install the APK on a device
    • Enable ChatLogLevel.VERBOSE — the stack trace in Logcat will show which class was not found
    • Add the missing class to -keep rules, rebuild

How to verify

  • After adding the rules, the release build passes a smoke test: opening the chat, sending a message, receiving a message — without a FATAL EXCEPTION in Logcat.
  • In Crashlytics/Sentry, no new events with ClassNotFoundException/NoSuchMethodException/JsonSyntaxException from the edna.chatcenter.* packages appear within 24 hours after the release.

Push notifications arrive unpredictably

Some push messages reach the device, some are lost (four to seven out of ten); or delivery comes with a minute delay after a long background; or the first push after a device reboot is lost before the app is opened. Possible reasons:

  1. Race with cold SDK startup. With synchronous init(), an early push is blocked until initialization completes and is processed afterwards. If on a full cold start of Application the first push arrives before init() and the app closes immediately after arrival — the push is lost. The solution is initAsync(): the push is queued and delivered when the SDK is ready.

  2. Duplicates with the same message_id. The SDK deduplicates push by message id; a repeated push with the same id is silently ignored. Deduplication decisions are visible in VERBOSE logs.

  3. OEM power-saving restrictions. Xiaomi MIUI, Huawei, Samsung, and others may apply their own policies (app whitelist, "protected apps", "sleeping apps"). In the help for end users, describe how to add your app to the whitelist.

In the background with priority: high, 90%+ of messages get through; with priority: normal, the push arrives on the next active app usage.


The user refused the permission (CAMERA / RECORD_AUDIO / Storage)

Symptoms

  • The corresponding button in the chat does not respond to taps
  • The system permission request dialog is no longer shown
  • The chat logic works, but file attachment / voice recording / photo capture does not

Checklist

  1. Did a preventive explanation dialog appear before the system request?

    • If permissionsDescriptionDialogsEnabled = true is enabled in ChatConfig, the SDK first shows its own dialog explaining why the permission is needed, and only then — the Android system dialog
    • If the flag is off, the system dialog appears immediately
    • The style of the preventive dialog is configured via PermissionDescriptionPopupStyle — see Design system
  2. After two refusals: does the SDK offer to go to app settings?

    • After "Don't ask again", Android 11+ switches the permission to "Denied permanently" mode
    • For CAMERA and storage: the SDK shows an AlertDialog with a "Settings" button, which opens the app screen (Settings.ACTION_APPLICATION_DETAILS_SETTINGS). The integrator does not need to do anything manually.
    • For RECORD_AUDIO: the SDK shows only a Toast about the inability to record a message, without navigation to settings. In this case, prompt the user to go to settings in your own UI or handle the case through your own listener.
  3. Is the permission not requested at all?

    • The SDK requests the permission lazily — on the first attempt to use the corresponding feature (camera, microphone, files)
    • If the request does not appear on the button tap — the corresponding feature is disabled in ChatConfig (e.g., file attachment is forbidden by server settings)
  4. POST_NOTIFICATIONS not granted on Android 13+?

    • This only affects showing push notifications in the system shade, not push handling inside the SDK (handleFCMMessage / handlePushMessage work independently)
    • The SDK requests this permission on the first chat opening on Android 13+. If the user refused — push will be processed by the SDK, but not displayed in the system shade. If desired, you can additionally request the permission in your own UI (for example, on user sign-in) before opening the chat.

How to verify

  • In Settings → Apps → <app> → Permissions, the corresponding permission is in the Allow or Ask every time state.
  • The attach button / microphone / camera in the chat opens the corresponding UI without delay.
  • A table of all SDK runtime permissions and the scenarios in which they are requested is in Android permissions.

The unread counter does not update

Symptoms

  • The badge shows a stale value
  • unreadMessageCountChanged() is not called

Checklist

  1. Is the user authorized?

    • The counter only works after a successful authorize(). This is the most common cause — check it first.
  2. Is ChatCenterUIListener set?

    chatCenterUI.setChatCenterUIListener(object : ChatCenterUIListener {
    override fun unreadMessageCountChanged(count: UInt) {
    updateBadge(count.toInt())
    }
    })
    note

    The parameter type is kotlin.UInt. To use from Java, you will need to obtain an int representation via reflection or a wrapper method on Kotlin: direct override from Java is not possible due to name mangling.

  3. Is keepWebSocketActive = true if the chat is closed?

    • Without an active WebSocket, the counter is updated only when the chat is opened
    • The parameter is marked deprecated, the resulting behavior is controlled by the server — see ChatConfig

How to verify

  • In Logcat, on receiving a new message, unreadMessageCountChanged is called with an increased count.
  • On opening the chat and reading all messages, unreadMessageCountChanged is called with count = 0u.

Collecting logs for support

Personal data in logs

At the ChatLogLevel.VERBOSE level, the SDK logs, among other things, message texts, user identifiers, authorization metadata, and request URLs. Before sending logs to support@edna.io:

  1. Obtain user consent (or use a depersonalized test account) — this is a GDPR/152-FZ requirement.
  2. Remove authorization tokens (Authorization: Bearer ..., JWT in payload) — they grant access to the user's session.
  3. Mask conversation text or use a reproduction on synthetic data.

Do not send raw VERBOSE logs from a client's production device without legal coordination.

If the issue is not resolved, collect logs and send them to support@edna.io:

  1. Enable the maximum logging level: ChatLoggerConfig(applicationContext, logLevel = ChatLogLevel.VERBOSE) (pass the config to the ChatCenterUI constructor).
  2. Reproduce the issue.
  3. Export the logs: via the Shake gesture in the chat, via adb logcat | grep ELog, or from files in {filesDir}/logs/. For sending from release builds without physical access to the device, use ChatLoggerConfig.LogInterceptor (Crashlytics/Sentry/AppMetrica).
Access to log files

On Android 11+ (API 30+), the data/data/{package}/files/ directory is not accessible via ADB for release builds (debuggable = false). Use the Shake gesture, LogInterceptor, or sending logs from the app itself.

A detailed logger configuration, formats, and LogInterceptor examples — Logger.


Problem not resolved?

If none of the checklists helped, contact support at support@edna.io. Before contacting, try to reproduce the issue on a minimal test project — if the problem does not reproduce there, this helps localize the defect in the integration rather than in the SDK.