Troubleshooting Guide
This guide compiles common issues encountered during the integration and operation of the tk_ota_push SDK and the native runtime, complete with root causes in the codebase and step-by-step solutions.
1. Patch is Not Downloaded or Does Not Apply
Symptom A: TkOtaPush.instance.isAvailable Returns false
- Cause: The application was launched in debug mode (
flutter run) or compiled using standard Flutter instead of the Shorebird toolchain (tk-ota-push release). - Solution: Always build the baseline Store binary using
tk-ota-push releaseand install the resulting APK/AAB or IPA in release mode.
Symptom B: outcome.executionMode is blockedByGatekeeper (networkCallPerformed: false)
- Cause: One of the 3 cumulative conditions in
RemoteConfigGatekeeperevaluated to false. Inspectoutcome.gatekeeperEvaluation?.verdictandoutcome.message:GatekeeperVerdict.skippedVersionMismatch: version passed toTkOtaPush.init(appReleaseVersion: ...)does not matchtarget_release_versionin the Remote Config payload.GatekeeperVerdict.skippedNoPatchAvailable:patch_availableisfalsein the Remote Config payload.GatekeeperVerdict.skippedAlreadyUpToDate: the currently active patch (currentPatchNumber) is greater than or equal tolatest_patch_number.
- Solution: Align
appReleaseVersionwith the exact version inpubspec.yaml(e.g.,'1.0.0+3') and bumplatest_patch_numberin your Remote Config provider.
Symptom C: Patch Downloaded but UI Does Not Change Immediately
- Cause: By architecture, the C++/Rust engine loads the new snapshot (
application binaryorpatch artifact) during Dart VM initialization on cold start (Cold Start). Backgrounding and foregrounding the app (Warm Resume) does not reload the shared native library. - Solution: Completely terminate (cold-kill) and reopen the app (or programmatically restart following a critical update where
outcome.restartRequired == true).
2. Error Update rejected: hash mismatch or CorruptPatch
- Cause: The
application binary(Android) orApp(iOS) binary installed on the device does not have the same SHA-256 fingerprint as the reference Release recorded on the OTA server. This occurs if you ranflutter build apkmanually after runningtk-ota-push release, or if an unstripped intermediate file was used. - Solution:
- Use
tk_ota_push>= 0.5.1/1.0.0, wherePublisher._extractLibAppFromPackageextractsapplication binarydirectly from the final.apkor.aabarchive. - Distribute to users strictly the
.apk,.aab, or.ipaproduced bytk-ota-push release, without running subsequent manual build commands.
- Use
3. Error InvalidSignature (Patch signature could not be verified)
- Cause: The Rust engine rejected the patch because the RSA signature (
hash_signature) returned byPOST /api/v1/patches/checkdoes not validate againstpatch_public_keyembedded insideflutter_assets/shorebird.yaml(failure_category: "invalid_signature"). - Solution:
- Verify
shorebird.yamlis declared underflutter.assetsinpubspec.yaml. - Ensure the RSA signing key in cryptographic signing engine was not regenerated with a different keypair since the Store Release was published.
- When running
tk-ota-push release, the CLI automatically synchronizespatch_public_keyfromGET /api/admin/v1/public-key.
- Verify
4. Bootloop Crash on Patch Boot & Active Rollback Alert
- Cause: Code introduced in the patch causes a fatal unhandled exception at boot time, or reads an incompatible local database/preferences schema following a rollback.
- Automatic Behavior:
- On the subsequent launch,
handle_prior_boot_failure_if_necessary()observes thatcurrently_booting_patchfailed to finish booting. - The Rust engine flags the patch as
PatchKnownBad, rolls back to the previous healthy binary, and dispatches__patch_install_failure__(crash_recovery: patch N failed to boot). - On the Dart side,
OtaRollbackTrackerfiresonRollbackDetected(OtaRollbackEvent).
- On the subsequent launch,
- Solution:
- Implement
onRollbackDetectedinTkOtaPush.initto wipe obsolete local caches (SharedPreferences, SQLite, Hive) upon regression. - Fix the Dart bug and publish a newer patch with an incremented number (
N + 1). - In the Web Console (
/support), acknowledge the rollback alert on impacted devices.
- Implement
5. Incorrect app_id or Environment Collision (dev / staging / prod)
- Cause: Published patches are not discovered because the
app_idbaked into the Release differs from theapp_idtargeted duringtk-ota-push patch. - Solution:
- Check the resolution cascade in
FlutterProject.resolveEffectiveAppId: if you built the Release using--flavor prod, pass--flavor prodduringtk-ota-push patch. - Inspect the effective value injected into the header of
shorebird.yamlfollowing the build.
- Check the resolution cascade in
6. Unreachable Server & Android HTTP Cleartext Blocking
- Symptom: No network calls reach the TkushPatch Server backend (
ShorebirdUpdaterController) when testing on an Android device over local Wi-Fi withbase_url: "http://192.168.x.x:8080", and telemetry logsfailure_category: "network". - Causes & Solutions:
- Missing
INTERNETPermission: declare<uses-permission android:name="android.permission.INTERNET"/>insideandroid/app/src/main/AndroidManifest.xml. - Android 9+ Cleartext Blocking: for local development over unencrypted
http://, setandroid:usesCleartextTraffic="true"on the<application>tag inAndroidManifest.xml. - Using
localhostinbase_url: on a physical smartphone,localhostresolves to the phone itself rather than your dev workstation. Use your machine's LAN IP inshorebird.yaml(e.g.,http://192.168.0.199:8080). The TkushPatch Server backend dynamically rewrites download URLs viaShorebirdUpdaterController.resolveClientReachableUrl.
- Missing
7. NATIVE CODE DIVERGENCE DETECTED Blocking During patch
- Symptom:
tk-ota-push patchaborts with the message:NATIVE CODE DIVERGENCE DETECTED (classes.dex, manifest or C++ libs modified)(orInfo.plist or Frameworks modifiedon iOS). - Cause:
NativeIntegrityScannerdetected that native machine code (nativeCodeHash) changed compared to the locked Release on the server (added a Flutter plugin with Kotlin/Swift/C++ code, or modifiedAndroidManifest.xml/Info.plist). - Solution:
- The OTA engine can only patch the Dart layer. Increment your version in
pubspec.yamland publish a new Store Release (tk-ota-push release). - If you are certain the native change is non-breaking for Dart execution, bypass using
--force(not recommended in production).
- The OTA engine can only patch the Dart layer. Increment your version in
8. HTTP 409 CONFLICT - RELEASE OVERWRITE PROTECTION
- Symptom:
tk-ota-push releasefails withHTTP 409 CONFLICTstating that the Release is already locked. - Cause:
Publisher.guardNoOverwriteprohibits overwriting an existing Release with a new build, because recompiling under the same version modifies baseline memory layouts and would trigger segmentation faults (Segfault) on devices running the previously compiled binary. - Solution:
- To patch the existing version, execute
tk-ota-push patch. - To publish a new Store version, bump the build number in
pubspec.yaml(e.g.,1.0.0+2to1.0.0+3) before runningtk-ota-push release.
- To patch the existing version, execute