# Troubleshooting

## Diagnostics-first workflow

When Freshdesk integration issues are unclear, run diagnostics before changing code:

```typescript
import FreshdeskSDK from '@freshworks/react-native-freshdesk-sdk';

await FreshdeskSDK.enableDebugLogs(true);
const report = await FreshdeskSDK.runDiagnostics();
console.log(report.prettyPrinted);
```

Process checks in order: any `fail` first, then `warn`, then `skipped`. Apply each check's
`fixHint` verbatim. Re-run diagnostics after every fix until `overallStatus` is `pass` and
the symptom is resolved.

On **iOS**, diagnostics cover config, network, remote config, JWT, push, widget, and runtime
checks (requires FreshdeskSDK 1.3+). On **Android**, the wrapper returns
integration checks plus `runtime.diagnostics: skipped` — use `debugMode: true` on
`initialize()` and inspect Logcat for native SDK logs.

For AI-assisted integration and debugging, copy the **AI Integration Kit** from
`node_modules/@freshworks/react-native-freshdesk-sdk/ai-integration-kit/` into your app repo.
See [ai-integration-kit/README.md](../../ai-integration-kit/README.md).

### Android `openSupport()` Spinner / Blank Widget

**Problem:** Diagnostics show `runtime.sdkInitialized: pass`, but the widget opens with an endless
loading spinner or blank content.

**Common causes:**

1. **Host missing scheme** — Host was `yourcompany.freshdesk.com` instead of
   `https://yourcompany.freshdesk.com`. The SDK auto-prefixes `https://` from wrapper **1.2.2+**;
   on older versions, update `.env` manually.
2. **Double initialization on Android** — Native `FreshdeskSDK.initialize()` in
   `MainApplication.onCreate()` **and** JS `FreshdeskSDK.initialize()` conflict. Diagnostics may
   show `runtime.doubleInit: warn`.
3. **JWT enforced but no token** — User state stays `jwtNotPresent`; widget cannot load content.

**Solutions:**

1. Prefer `FRESHDESK_HOST=https://yourcompany.freshdesk.com` in `.env` (bare domains also work on 1.2.2+).
2. **Remove** native init from `MainApplication.onCreate()`. Use JS init for in-app support. For
   headless FCM only, initialize natively inside `FirebaseMessagingService` (see sample app).
3. Pass a valid `jwt` at init when the widget enforces JWT.

### iOS `pod install` Fails — `native-versions.json` Not Found

**Problem:**

```text
No such file or directory @ rb_sysopen - .../native-versions.json
```

**Cause:** Published npm tarball before **1.2.2** omitted `native-versions.json`, which the
podspec reads for the iOS SPM pin.

**Solution:** Upgrade to `@freshworks/react-native-freshdesk-sdk@1.2.2` or later, then
`cd ios && pod install`.

## Common Issues

### "Native module not found" Error

**Problem:** The app throws "Freshdesk native module not found" error.

**Solutions:**

1. Ensure the library is installed:
   ```bash
   npm install @freshworks/react-native-freshdesk-sdk
   ```

2. iOS - Reinstall pods:
   ```bash
   cd ios && pod install
   ```

3. Clean and rebuild:
   - iOS: `rm -rf ios/build ios/Pods && cd ios && pod install`
   - Android: `cd android && ./gradlew clean`

4. Restart the Metro bundler and rebuild the app.

### App Shows "Could not connect to development server"

**Problem:** The app builds and installs, then shows a red error screen with:

```
Could not connect to development server.
URL: http://localhost:8081/index.bundle?platform=android...
java.net.ConnectException: Failed to connect to localhost/127.0.0.1:8081 ... ECONNREFUSED
```

**Cause:** This is a runtime (not build) issue — the native app installed fine, but it can't
reach the Metro JS bundler. Either Metro isn't running on port 8081, or the device can't route
`localhost:8081` to your machine.

**Solutions:**

1. Start Metro and leave it running in its own terminal:
   ```bash
   cd sample_app && npm start
   ```
   `npm run android` only builds/installs the app; it expects Metro to be running separately.

2. For a USB-connected Android device/emulator, forward the port so `localhost` resolves to your
   machine:
   ```bash
   adb reverse tcp:8081 tcp:8081
   ```
   Confirm the device is attached first with `adb devices`.

3. For a physical device on the same Wi-Fi, set **Dev Settings → Debug server host & port** to
   your machine's IP and port (e.g. `10.0.1.5:8081`).

4. Once Metro serves the bundle (`BUNDLE ./index.js ... 100.0%`), reload the app: press `r` in the
   Metro terminal, or shake the device and tap **Reload**.



**Problem:** `pod install` fails, or the app fails to compile/link against the Freshdesk SDK.

**Solutions (2.0.0+):**

1. Ensure your app's `Podfile` uses `platform :ios, '15.0'`. The native SDK
   requires iOS 15.0; a lower deployment target causes a CocoaPods
   "deployment target" error.

2. Nothing else Freshdesk-specific should be needed — the native SDK is a
   vendored, statically-linked xcframework, no `use_frameworks!`/SPM
   required. If you see linker errors or `no such module 'Expo'`, that's
   almost always leftover `~1.4.x` Podfile lines — see
   [iOS Linkage / Expo](#ios-linkage--expo-no-such-module-expo-mixed-staticdynamic-frameworks)
   below.

3. Ensure CocoaPods is recent:
   ```bash
   pod --version   # 1.12+
   ```

4. Clean and reinstall:
   ```bash
   rm -rf ios/build ios/Pods ios/Podfile.lock
   rm -rf ~/Library/Developer/Xcode/DerivedData
   cd ios && pod install
   ```

> **Still on `~1.4.x`?** That version consumed the native iOS SDK as a
> **Swift Package** via the podspec's `spm_dependency` helper (RN 0.75+,
> default), pinned in `native-versions.json`, requiring
> `use_frameworks! :linkage => :dynamic` (or `FRESHDESK_IOS_USE_VENDORED=1`
> as an opt-out). If you're on RN 0.75+ with that SPM path and see
> resolution errors, verify network access to
> `github.com/freshworks-oss/freshdesk-ios-sdk` and that the pinned version
> tag exists — or upgrade to 2.0.0+, which removes the SPM path (and this
> whole class of problem) entirely.

### iOS Linkage / Expo (`no such module 'Expo'`, mixed static/dynamic frameworks)

**Applies to 2.0.0+.** The native SDK now ships as a vendored
`FreshdeskSDK.xcframework` and the podspec builds it as a **static** framework.
`use_frameworks!` / `cocoapods-spm` / `spm_dependency` are **no longer required**
and their presence (left over from a 1.4.x integration) is now the usual cause of
iOS build breakage.

**`no such module 'Expo'`** — caused by forcing dynamic-framework linkage in an
Expo app. Fix:

1. Remove `use_frameworks!` and the Freshdesk SPM lines from the `Podfile` /
   config plugin.
2. `npx expo prebuild --clean && cd ios && pod install`

Expo returns to its default static linkage and the error is gone.

**Bare React Native** — remove from the `Podfile` / `Gemfile` the lines that only
existed for the 1.4.x SPM path:

| Remove | Why |
|---|---|
| `use_frameworks! :linkage => :dynamic` | not required by Freshdesk 2.0.0 |
| `gem "cocoapods-spm"` / `plugin 'cocoapods-spm'` | SDK no longer uses `spm_dependency` |
| `spm_dependency` lines added for Freshdesk | same |
| `ENV['FRESHDESK_IOS_USE_VENDORED'] = '1'` | vendored is the only path; the env gate was removed |
| `post_install` overrides added for mixed static/dynamic linkage | cause removed |

Then `cd ios && rm -rf Pods Podfile.lock && pod install`.

**If you still need `use_frameworks!`** for other SDKs (Firebase, Google Maps,
etc.), keep it but prefer `:linkage => :static`. The Freshdesk podspec sets
`s.static_framework = true`, so a static build is the expected configuration.

### iOS Build Fails with `'FreshdeskReactNative-Swift.h' file not found`

**(1.4.x only — see the 2.0.0+ section above for current guidance)**

**Problem:** The build fails compiling `FreshdeskNativeModule.mm` with
`'FreshdeskReactNative-Swift.h' file not found`, even though the Swift sources compile fine.

**Cause:** With `use_frameworks! :linkage => :dynamic`, the module builds as a framework and the
auto-generated Swift→ObjC header lives **inside** it, so it must be imported as
`<FreshdeskReactNative/FreshdeskReactNative-Swift.h>`. Importing it without the module prefix
(`<FreshdeskReactNative-Swift.h>`) or as a quoted header only resolves for static linkage.

**Solution:** Update to a release that imports the header with the framework-qualified path
(the bridging file tries `<FreshdeskReactNative/FreshdeskReactNative-Swift.h>` first, then falls
back for static linkage). A clean rebuild does **not** fix it on its own, since the cause is the
import path, not stale artifacts.

### iOS Build Fails with `SDK does not contain 'libarclite'`

**Problem:** The build fails with
`SDK does not contain 'libarclite' at the path '.../usr/lib/arc/libarclite_iphoneos.a'; try increasing the minimum deployment target`.

**Cause:** Recent Xcode versions removed the legacy `libarclite` archive. A pod whose deployment
target is older than iOS 13 still tries to link it.

**Solution:** Pin every pod target's deployment target in your `Podfile`'s `post_install`, then
re-run `pod install`:

```ruby
post_install do |installer|
  # ... existing react_native_post_install(...) call ...
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
    end
  end
end
```

### App Crashes at Launch: `Library not loaded: @rpath/FreshdeskSDK.framework/FreshdeskSDK`

**(1.4.x only.)** On **2.0.0+** the native SDK is a vendored, statically-linked
xcframework embedded by CocoaPods' standard `[CP] Embed Pods Frameworks`
phase — there is no separate SPM product that can fail to embed, so this
cannot happen. Upgrade instead of chasing the fix below.

**Problem:** The app builds and installs, then crashes immediately on launch with
`dyld: Library not loaded: @rpath/FreshdeskSDK.framework/FreshdeskSDK ... (no such file)`.

**Cause:** The native SDK is pulled in as a Swift Package via the podspec's `spm_dependency`.
That helper links `FreshdeskSDK.framework` into the `FreshdeskReactNative` pod, but it does **not**
embed `FreshdeskSDK.framework` into the app bundle. CocoaPods' "Embed Pods Frameworks" phase only
copies pod frameworks, not SPM products — so the dynamic framework is missing at runtime.

**Solution:** Require the helper shipped in the npm package (SDK **1.2.3+**) and call it from
`post_install`:

```ruby
require_relative '../node_modules/@freshworks/react-native-freshdesk-sdk/ios/freshdesk_post_install'

post_install do |installer|
  # ... existing react_native_post_install(...) call ...
  freshdesk_post_install(installer)
end
```

After updating the `Podfile`, run `pod install` and rebuild. Verify the framework is embedded:
`ls <app>.app/Frameworks/` should list both `FreshdeskReactNative.framework` and
`FreshdeskSDK.framework`.

### iOS Icons / Custom Fonts Render as "?" (Missing Glyphs)

**Problem:** On iOS, `react-native-vector-icons` (or any bundled custom font) renders as `?`
placeholder boxes, even though `UIAppFonts` lists the `.ttf` in `Info.plist`. Android renders
the same icons correctly.

**Cause:** This happens whenever the app's `Podfile` enables
`use_frameworks! :linkage => :dynamic` for its own reasons (Freshdesk 2.0.0+
does not require or enable it itself). With dynamic frameworks, a pod's font
resources (e.g. `react-native-vector-icons`' `s.resources = "Fonts/*.ttf"`)
are bundled **inside that pod's framework** (`RNVectorIcons.framework`) instead of the main app
bundle. iOS only loads fonts declared in `UIAppFonts` from the **main app bundle**, so the lookup
fails and glyphs fall back to `?`. The CocoaPods "[CP] Copy Pods Resources" phase does not copy
the font into the app bundle under `use_frameworks!`.

**Solution:** Bundle the font into the **app target** directly so it lands in the main bundle:

1. Copy the font(s) you use into your iOS project, e.g.
   `ios/<app>/Fonts/MaterialCommunityIcons.ttf`
   (source: `node_modules/react-native-vector-icons/Fonts/`).
2. Add the font to the app target's **Copy Bundle Resources** build phase (drag it into the
   target in Xcode, or add it to the target's `Resources` phase in `project.pbxproj`).
3. Declare it in the app's `Info.plist` under `UIAppFonts`:
   ```xml
   <key>UIAppFonts</key>
   <array>
     <string>MaterialCommunityIcons.ttf</string>
   </array>
   ```
4. Rebuild. Confirm the font is in the bundle: `ls <app>.app/MaterialCommunityIcons.ttf`.

> Tip: `npx react-native-asset` (with a `react-native.config.js` `assets` entry) automates
> steps 1–3. Only bundle the specific font files your app actually imports to keep the app size down.

### Android Build Fails: Kotlin Metadata Version Mismatch

**Problem:** `:app:compileDebugKotlin` fails with:

```text
Module was compiled with an incompatible version of Kotlin.
The binary version of its metadata is 2.1.0, expected version is 1.9.0.
```

**Cause:** Freshdesk Android native SDK 2.2.x uses Kotlin 2.1 metadata. React Native 0.75 uses
Kotlin 1.9.x. The host `:app` module compiles against Freshdesk without
`-Xskip-metadata-version-check`.

**Solution:** Apply the consumer Gradle snippet from the npm package (SDK **1.2.3+**) in root
`android/build.gradle`:

```gradle
apply from: file("../../node_modules/@freshworks/react-native-freshdesk-sdk/android/freshdesk-consumer.gradle")
```

Also set `kotlinOptions` on the app module (see [installation.md](installation.md)). Do **not**
bump `kotlinVersion` to 2.1 on RN 0.75 — it breaks the React Native Gradle plugin.

### Android Build Fails with `26.0.1`

**Problem:** `./gradlew` fails immediately with `What went wrong: 26.0.1` and no other context.

**Cause:** Gradle 8.8 (and the embedded Kotlin compiler) does not support running on JDK 26. This happens when `JAVA_HOME` points to a JDK newer than 21.

**Solutions:**

1. Build with JDK 17 (recommended for React Native Android):
   ```bash
   export JAVA_HOME=$(/usr/libexec/java_home -v 17)
   cd sample_app/android && ./gradlew clean
   cd .. && npx react-native run-android
   ```

2. Pin JDK 17 for Gradle only by adding this to `sample_app/android/gradle.properties` (use your local JDK 17 path):
   ```properties
   org.gradle.java.home=/Users/you/Library/Java/JavaVirtualMachines/ms-17.0.19/Contents/Home
   ```

3. Make JDK 17 the default in your shell profile so Android builds always use it:
   ```bash
   export JAVA_HOME=$(/usr/libexec/java_home -v 17)
   ```

### Android Build Fails with Kotlin "Type mismatch" in `react-native-screens` / `react-native-gesture-handler`

**Problem:** `compileDebugKotlin` fails for `:react-native-screens` and/or
`:react-native-gesture-handler` with errors like:

```
ScreenViewManager.kt: Type mismatch: inferred type is StateWrapper? but StateWrapper was expected
RNGestureHandlerTouchEvent.kt: Type mismatch: inferred type is View? but View was expected
```

**Cause:** These libraries were compiled against an older React Native API surface. React Native
0.75 tightened nullability annotations on core APIs (`ViewManager.updateState`'s `StateWrapper`
parameter and `UIManagerHelper.getSurfaceId(View)`), so versions of these libraries released
before RN 0.75 no longer compile. This is a **library-version mismatch**, not a Kotlin toolchain
issue (RN 0.75 already uses the correct Kotlin 1.9.24).

**Solution:** Upgrade both navigation libraries to RN 0.75-compatible versions in your app's
`package.json`, then reinstall:

```jsonc
{
  "dependencies": {
    "react-native-screens": "3.34.0",         // first release with RN 0.75 support
    "react-native-gesture-handler": "2.20.2"   // RN 0.75 supported (>= 2.18.0)
  },
  "overrides": {
    "react-native-gesture-handler": "2.20.2"
  }
}
```

```bash
npm install
cd android && ./gradlew clean
```

The `sample_app` is pinned to these versions for RN 0.75.4.

### Android "Could not resolve" Dependency Error

**Problem:** Gradle fails to resolve `com.freshworks.sdk:freshdesk`.

**Solutions:**

1. Verify `mavenCentral()` is in repositories:
   ```gradle
   allprojects {
       repositories {
           google()
           mavenCentral()
       }
   }
   ```

2. Sync project with Gradle files in Android Studio.

3. Check network connectivity to Maven Central.

### SDK Initialization Fails

**Problem:** `initialize()` throws an error or never completes.

**Cause:** Initialization requires only `token`, `host`, and `sdkId`. Missing push notification
configuration (Firebase, APNs, portal push keys) does **not** block initialization — in-app support
works without push. If `initialize()` rejects with `FRESHDESK_INIT_ERROR`, check network
connectivity and that credentials are valid. On Android, the promise now rejects explicitly when
remote config fetch fails instead of hanging indefinitely.

**Solution:**

1. Verify `token`, `host`, and `sdkId` from Admin Settings → Mobile Chat SDK.
2. Confirm network access from the device/emulator.
3. For JWT-enforced widgets, pass a valid `jwt` at init.
4. Do not treat missing Firebase/`google-services.json` or APNs setup as an init blocker — add
   push wiring only when you need tray notifications.
5. Check console for specific error messages:
   - `FRESHDESK_INVALID_CONFIG` - Missing parameters
   - `FRESHDESK_INIT_ERROR` - Server/connection issue or remote config fetch failure

### Events Not Firing

**Problem:** Event listeners not receiving events.

**Solutions:**

1. Ensure SDK is initialized before setting listeners.

2. Check listener is properly set up:
   ```typescript
   const subscription = FreshdeskSDK.addUnreadCountListener((event) => {
     console.log(event);
   });
   ```

3. Clean up and re-add listeners after app state changes.

4. On Android, verify `LocalBroadcastManager` is working.

5. On iOS, ensure you are on a release where the native module registers itself as the
   `RCTEventEmitter` (the Objective-C `FreshdeskNativeModule` sets the emitter on the Swift
   bridge in `-init`). On older builds the emitter was never wired up, so native notifications
   (`unreadCountChanged`, `userStateChanged`, `userCreated`, `onLinkPressed`) were silently
   dropped. A common symptom is the **initial unread count showing correctly** (it comes from the
   direct `getUnreadCount()` call) while **real-time updates never arrive** and **User State stays
   `unknown`** (both rely on events). Update to a fixed release and rebuild.

### openSupport() Shows Blank Screen

**Problem:** Support screen opens but is blank.

**Solutions:**

1. Ensure SDK is initialized successfully before calling `openSupport()`.

2. Check iOS deployment target is 15.0+.

3. Verify network connectivity.

4. Check Freshdesk credentials are valid.

### openSupport() / trackEvent() / setUserProperties() Silently Do Nothing Right After initialize() (iOS)

**Problem:** `initialize()` resolves without throwing, but the very next
call — `openSupport()`, `trackEvent()`, `setUserProperties()`, or
`setTicketProperties()` — appears to do nothing: no UI opens, no error is
thrown, no data reaches the Freshdesk dashboard. Often intermittent — it
works on a retry a moment later, or more reliably on a fast network. The
native SDK may log:

```
⚠️ [Freshdesk Warning] Tasks will be executed once the SDK is loaded, Check
whether the SDK is initialised with proper configuration.
```

**Cause:** The native iOS SDK's `Freshdesk.initialize(with:)` has no
completion callback, `async` variant, or any readiness signal of its own —
it does its own async internal loading after `initialize()` returns, and
any call made before that finishes is silently queued/dropped by the native
SDK itself (not by this wrapper). On SDK versions before the fix noted
below, the wrapper resolved `initialize()`'s JS promise the instant the
native call *returned*, not when the SDK was actually ready, so a call
issued right after `await initialize()` — completely normal usage — could
race the real load and lose.

**Solution:** Upgrade to an SDK version with the `initialize()` settle-delay
fix (see `CHANGELOG.md`'s "iOS `initialize()` settle delay" entry) —
`initialize()` now resolves only once the SDK has had time to actually
finish loading, so plain `await initialize()` usage becomes safe on its own;
no app code change needed. **Expect `initialize()` to take at least ~2
seconds on iOS as a result** — that is expected, not a regression.

On a version without the fix, or as a belt-and-braces measure: add a short
delay (1–2s) after `initialize()` resolves before making the first other
SDK call.

### JWT Authentication Issues

**Problem:** User state stuck in `notAuthenticated` or `authExpired`.

**Solutions:**

1. Verify JWT is correctly formatted and signed.

2. Check JWT is not expired.

3. For JWT-enforced SDKs, ensure widget settings match:
   - Go to Admin Settings -> Mobile Chat SDK
   - Verify "Enforce JWT" is enabled/disabled as expected

4. Listen for `userStateChanged` events to debug.

### Push Notifications Not Working

**Problem:** Not receiving push notifications from Freshdesk.

**Solutions (iOS):**

1. Confirm the **Push Notifications** capability + **Background Modes → Remote notifications** are
   enabled on the app target in Xcode. The Push Notifications capability links
   `quickbasket/quickbasket.entitlements` (`aps-environment`); without it,
   `didFailToRegisterForRemoteNotificationsWithError` fires.
2. Ensure the SDK is **initialized natively** in `AppDelegate didFinishLaunchingWithOptions`
   (via `FreshdeskPush.initialize`). The APNs token is registered at launch, before the JS layer
   runs `initialize()`; `setPushRegistrationToken` is a no-op until the SDK is initialized, so
   JS-only init drops the token. Verify the `Freshdesk*` keys exist in `Info.plist`.
3. Upload the APNs **`.p8`** auth key (with Key ID + Team ID) in the Freshdesk portal.
4. Test on a **real device** — the Simulator never receives remote pushes.
5. Verify the device token reaches the SDK: `AppDelegate` →
   `didRegisterForRemoteNotificationsWithDeviceToken` → `FreshdeskPush.registerToken(...)`. If
   `didFailToRegisterForRemoteNotificationsWithError` fires, the capability/provisioning is missing.

### Push Received in Foreground but Not in Background / Killed State (iOS)

**Problem:** Freshdesk notifications appear while the app is open (foreground), but nothing
arrives when the app is backgrounded or killed.

**Cause:** `AppDelegate` only forwards pushes from `willPresentNotification:` (foreground) and
`didReceiveNotificationResponse:` (notification tapped). When the app is backgrounded or
relaunched in the background, iOS delivers the payload through
`application:didReceiveRemoteNotification:fetchCompletionHandler:`. If that method is not
implemented (or it does not call `FreshdeskPush.handleRemoteNotification`), the SDK never sees the
background payload and no notification is processed/displayed.

**Solution:**

1. Implement the background delivery handler in `AppDelegate` and forward Freshdesk payloads to
   the SDK (the `sample_app` includes this):

   ```objc
   - (void)application:(UIApplication *)application
       didReceiveRemoteNotification:(NSDictionary *)userInfo
             fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
   {
     if ([FreshdeskPush isFreshdeskNotification:userInfo]) {
       [FreshdeskPush handleRemoteNotification:userInfo
                             applicationState:application.applicationState];
       completionHandler(UIBackgroundFetchResultNewData);
       return;
     }
     completionHandler(UIBackgroundFetchResultNoData);
   }
   ```

2. Confirm `UIBackgroundModes` includes `remote-notification` in `Info.plist` and that
   **Background Modes → Remote notifications** is enabled on the app target. iOS only wakes a
   backgrounded/terminated app for the handler above when this mode is set.

3. The push payload must include `content-available: 1` in its `aps` dictionary for iOS to wake
   the app in the background. Freshdesk sends this for its data pushes; if you have a custom push
   proxy, make sure it is preserved.

4. Note the user-force-quit caveat: if the user **manually swipes the app away** from the app
   switcher, iOS suppresses background `content-available` wakeups until the app is launched again.
   A visible alert push still appears (rendered by the system), and tapping it launches the app and
   routes through `didReceiveNotificationResponse:`. This is expected iOS behavior, not an SDK bug.

**Solutions (Android):**

1. Ensure **`google-services.json`** is in `android/app/` and the **Google Services plugin** is
   enabled (uncomment the classpath in `android/build.gradle` and `apply plugin` in
   `android/app/build.gradle`).
2. Upload your **FCM credentials** in the Freshdesk portal.
3. Confirm `FreshdeskMessagingService` is registered in `AndroidManifest.xml` and that the device
   has Google Play services.
4. On Android 13+, grant the `POST_NOTIFICATIONS` runtime permission.
5. Verify the token reaches the SDK in `FreshdeskMessagingService.onNewToken` →
   `FreshdeskSDK.setPushRegistrationToken(token)`.
6. Ensure the SDK is **initialized natively** in `MainApplication.onCreate()`. Push methods are
   no-ops until the SDK is initialized; initializing only from JavaScript (`FreshdeskContext`)
   means the token is registered before init completes (and dropped), and background/killed
   pushes are ignored because the JS layer never runs in a headless FCM delivery. Check Logcat for
   `FreshdeskPush: Freshdesk SDK initialized natively (push-ready)`; if you instead see
   `SDK is not initialized`, native init did not run (verify `.env` credentials are present and
   surfaced via `BuildConfig`).

### Push Shown In-App but Not in the Notification Tray (iOS)

**Problem:** Freshdesk notifications appear while the app is open (foreground / in-app), but
nothing shows in the system notification tray when the app is in the background or killed.

**Cause:** The Freshdesk SDK's push methods (`handleRemoteNotification`) only post a tray
notification once the SDK is initialized. When the app is **cold-launched in the background by a
push**, iOS calls `application:didReceiveRemoteNotification:fetchCompletionHandler:` right after
`didFinishLaunchingWithOptions` returns. If native init is dispatched asynchronously, that handler
can run **before** init finishes, so the payload is dropped and no tray notification is posted. The
same push works in the foreground because the SDK is already initialized.

**Solution:** Initialize the SDK **synchronously** at launch so it is push-ready before the
background delivery handler runs. The SDK's native entry point
(`FreshdeskNativeModuleBridge.initializeSDK`, driven by `FreshdeskPush.initializeSDK` from
`AppDelegate didFinishLaunchingWithOptions`) initializes synchronously on the main thread for
exactly this reason. If you wire init yourself, do not defer it to a later run-loop turn
(`DispatchQueue.main.async`) — run it inline in `didFinishLaunchingWithOptions` so it completes
before iOS delivers the launching push.

### Push Shown In-App but Not in the Notification Tray (Android)

**Problem:** Freshdesk notifications appear while the app is open (foreground / in-app), but
nothing shows in the system notification tray when the app is in the background or killed.

**Cause:** `FreshdeskSDK.initialize(...)` is **asynchronous** — its completion callback fires only
after the SDK is fully set up, and `handleFCMNotification` / `setPushRegistrationToken` are
**no-ops until then**. On a headless FCM delivery (app killed/background), Android starts the
process to run only `FreshdeskMessagingService`. `MainApplication.onCreate()` merely *starts*
the async init, so `onMessageReceived` can run and call `handleFCMNotification` **before init
finishes** — the message is dropped and no tray notification is posted. The same push works while
the app is already running because the SDK is already initialized.

**Solution:** Have the FCM service **wait for initialization to complete** before forwarding the
message. The `sample_app` routes init through a shared, idempotent `FreshdeskInitializer` that
both `MainApplication.onCreate()` and `FreshdeskMessagingService` use; the service calls
`FreshdeskInitializer.ensureInitializedBlocking(applicationContext)` before
`FreshdeskSDK.handleFCMNotification(data)` (and before `setPushRegistrationToken` in
`onNewToken`). `onMessageReceived` runs on a background thread, so the short, bounded wait is
safe. If you wire push yourself, guarantee the SDK is initialized before calling any push method
in the service.

## Error Codes Reference

Import the typed enum and compare against it instead of matching the string
literal directly:

```typescript
import { FreshdeskErrorCode } from '@freshworks/react-native-freshdesk-sdk';

try {
  await FreshdeskSDK.openSupport();
} catch (error) {
  if (error.code === FreshdeskErrorCode.NOT_INITIALIZED) { /* ... */ }
}
```

Coverage is **not identical on both platforms** — a code marked (Android) or
(iOS) is only ever rejected on that platform; unmarked codes can occur on
either.

| Error Code | Platform | Description | Solution |
|------------|----------|-------------|----------|
| `FRESHDESK_INVALID_CONFIG` | Both | `initialize()`'s config was missing a required key or malformed | Check token, host, sdkId are provided |
| `FRESHDESK_NOT_INITIALIZED` | Both | A method other than `initialize()` was called before `initialize()` resolved | Call `initialize()` first, await it |
| `FRESHDESK_INIT_TIMEOUT` | Android | `initialize()` timed out (20s) waiting for the native SDK to report ready | Verify token/host/sdkId, network reachability, JWT settings |
| `FRESHDESK_INIT_ERROR` | Android | `initialize()` threw before it could start | Check credentials and network |
| `FRESHDESK_NOT_READY` | Android | `initialize()` resolved but the native SDK isn't ready yet | Retry shortly — self-resolves |
| `FRESHDESK_NO_ACTIVITY` | Android | `openSupport()`/`openKnowledgeBase()`/`openTopic()` had no foreground Activity to attach to | Ensure app is foregrounded |
| `FRESHDESK_NO_VIEW_CONTROLLER` | iOS | `openSupport()`/`openKnowledgeBase()`/`openTopic()` had no view controller to present from | Ensure app is foregrounded |
| `FRESHDESK_OPEN_ERROR` | Android | `openSupport()`/`openKnowledgeBase()`/`openTopic()` failed to present the native UI | Check SDK is initialized; on iOS, see the [silent-failure section above](#opensupport--trackevent--setuserproperties-silently-do-nothing-right-after-initialize-ios) |
| `FRESHDESK_TRACK_ERROR` | Android | `trackEvent()` failed | Check SDK is initialized |
| `FRESHDESK_USER_PROPERTIES_ERROR` | Both | `setUserProperties()` failed | Check SDK is initialized |
| `FRESHDESK_TICKET_PROPERTIES_ERROR` | Android | `setTicketProperties()` failed | Check SDK is initialized |
| `FRESHDESK_AUTH_ERROR` | Android | `authenticateAndUpdate()` failed | Verify JWT is valid |
| `FRESHDESK_DISMISS_ERROR` | Android | `dismiss()` failed | Check SDK is initialized |
| `FRESHDESK_USER_ERROR` | Both | `getUser()` failed, or its response could not be parsed | Check SDK is initialized and network |
| `FRESHDESK_USER_PARSE_ERROR` | iOS | `getUser()`'s response could not be parsed | Report with SDK version — likely a native response-shape issue |
| `FRESHDESK_CONFIG_PARSE_ERROR` | Both | `setContentConfiguration()`'s JSON payload could not be parsed | Check the config object shape |

## Debug Mode

Enable debug logging on Android:

```typescript
await FreshdeskSDK.initialize({
  // ... config
  debugMode: true,
});
```

Check native logs:
- iOS: Xcode console or `Console.app`
- Android: Android Studio Logcat or `adb logcat`

## Getting Help

If issues persist:

1. Check [GitHub Issues](https://github.com/freshworks/freshdesk_react_native_sdk/issues)
2. Contact Freshdesk support: support@freshdesk.com
3. Include error logs and SDK version in your report
