Troubleshooting
Find why a device did not take a release, with the debug screen and the device probe, the eight common causes and their fix, and the rarer ones as one table.
A device that did not update has usually not checked since you released, runs the build from before your change, or is excluded on compatibility. Every other cause reports a reason code, and the two tools below show it.
A release, a rollout change, a pause, a resume or a cap change applies within about a minute (How an update reaches a device). A result that contradicts a change you made a moment ago is that delay, not a failure.
The debug screen
The SDK opens its debug screen when the app calls showDebugScreen(). Put the call behind a settings entry or a gesture of your own. The method is on the API reference of each SDK: React Native, Expo, Capacitor and Cordova.
| Section | Line | Tells you |
|---|---|---|
| Last check | Result | Tells you the status with its reason and condition as one code, such as skipped device_incompatible fingerprint. Results and reasons explains every code. |
| Last check | When | Tells you whether the device has checked since you released. |
| Channel | Channel id, Source | Tells you which channel the device follows: config from the build, runtime from setChannel(). |
| Releases | Running, Downloaded | Tells you the release the device runs, and one waiting for its apply moment. |
| Device | Device id, Binary, Fingerprint | Tells you the id --device takes, and the facts the probe needs without it. |
| Configuration | Debug builds, Built at | Tells you whether updates are off in debug builds, and the build time no earlier release is applied on. |
Check now runs one check and reloads the screen. Share copies the whole screen as text, the shape to paste into a support request.
The device probe
The probe runs the evaluation a device runs, from your machine, against the channel's live index. It needs a signed-in CLI. The device id is on the debug screen, on the device's page in the console, or in npx hotcodepush device list --attribute userId=42.
npx hotcodepush device probe --device 6b1e9d37-2f5c-4a80-9c46-d8e3a1f7b259Outcome is the answer: available with the release the device takes on its next check, or up_to_date with the release it runs. A skipped outcome carries the reason, the failing condition and the release it names. The table under it lists every release the index offers with each condition marked pass or fail.
Without --device the probe evaluates as a device on a given --platform, --binary-version and --binary-build would. Its Unknown line then lists what it treated as passing: the OS version, the rollout and often the fingerprint. At the spending cap a probe without --device reads as beyond the cap and lists no verdicts.
What the probe cannot show: build_debug, which it reads as available, and the download reasons and held restarts, which show on the device only. An Index line reading none: HTTP 404 at <url>; a device gets no release means the channel serves nothing. The options and the JSON output are on the device command.
Common causes
The release carries the previous build
- Symptom: the release shipped, the device took it, and nothing visibly changed, and the probe says
up_to_dateon the expected release. - Where it shows:
npx hotcodepush release get --release 43 --jsonprints the bundle'sgitSha,gitRefandisGitDirty. An unexpected commit, ortrue, means the upload took an older or uncommitted build. - Fix: build, then release, as one step:
npm run build && npx hotcodepush release create. Ship every commit makes it the pipeline's job.
The device has not applied it yet
- Symptom: the update is downloaded and waiting for its moment, or the device has not checked since you released. The default
applyStrategyisnext_start, so the update runs at the next cold start. - Where it shows: the sync result is
downloadedand the probe saysavailable. The debug screen shows a Downloaded release under the Running one, or a Last check older than the release. - Fix: close the app completely and open it again, or tap Check now on the debug screen. To change the moment, set
applyStrategyas Choose an apply strategy lays out, or release with--mandatory.
The native fingerprint does not match
- Symptom:
skipped,device_incompatible, conditionfingerprint. The fingerprint moves when a plugin or native package is added, removed or bumped, when the framework runtime changes and when a file underextraFingerprintPathschanges. - Where it shows: the probe's Outcome line, with
device.fingerprintbeside the condition'sfingerprintin--json.npx hotcodepush fingerprint diff store-build.json current.jsonnames the package that moved. - Fix: ship the store build that carries the new native layer, since devices on the old binary keep getting the releases built against it. Only when the change is known to be safe for the old binary, widen a release to it with
--binary-version.
The binary range excludes the device
- Symptom:
skipped,device_incompatible, conditionbinaryoros: the release was created with--binary-versionor--os-version, and this device's version is outside the range. - Where it shows: the probe, with the condition marked fail and its
rangebesidedevice.binaryVersionordevice.osVersionin--json. The OS version is unknown without--device. - Fix: release again with a range that includes the device, or without the condition.
npx hotcodepush release create --dry-runprints the audience a range reaches before anything is published.
The device follows another channel
- Symptom: no code. The release you expect is missing from the probe's verdicts, and the device is
up_to_dateon its own channel. A build follows one channel, fromhotcodepush.jsonorHOTCODEPUSH_CHANNELat build time, and the app may have moved it withsetChannel(). - Where it shows: the debug screen's Channel section, with Source
configorruntime.npx hotcodepush device get --device <id>prints the channel with its source. - Fix: release to the channel the device follows, or return the device with
setChannel(null). A test build that must follow its own channel gets it at build time. See Test with preview channels.
The rollout has not reached the device
- Symptom:
skipped,device_not_in_rollout: the release is staged, and this device's rollout bucket is above the current percentage. - Where it shows: the probe with
--device. Without the id the rollout counts as reached and the reason cannot show. The release's rollout, progression and the gate it waits on are innpx hotcodepush release get --release 43. - Fix:
npx hotcodepush release rollout --channel production --release 43 --rollout-percent 100, or leave the progression to finish. To reach one phone without moving the rollout, release to it with--device <id>, as Roll out gradually shows.
The release or its channel is paused
- Symptom:
skipped,channel_pausedfor a paused channel. A paused release is left out of the index, so there is no code: the probe saysup_to_dateand the release is missing from its verdicts. Auto-pause does this on its own when a release's failure rate passes the threshold. - Where it shows:
npx hotcodepush release get --release 43prints Statuspausedwith the counters behind it, andnpx hotcodepush channel get --channel productionprints "paused since". The pause also arrives by mail, in Slack or on your webhook. - Fix: fix what the devices hit first, then
npx hotcodepush release resume --channel production --release 43ornpx hotcodepush channel resume --channel production. A resume starts a new measurement window, so a still-broken release trips auto-pause again.
The release does not cover the platform
- Symptom: no code. The device reads an index without the release. On React Native and Expo a bundle is per platform, so a release of an iOS bundle is invisible to every Android device. On Capacitor and Cordova one bundle serves both platforms unless
--platformnamed one. - Where it shows: the probe with
--platform androidlacks the release that--platform ioslists.npx hotcodepush bundle get --bundle 17prints the bundle's platforms. - Fix: release for both platforms. On a Capacitor or Cordova app the second release moves zero bytes.
Rarer causes
| Reason | Cause | Fix |
|---|---|---|
release_older_than_binary | The release was created before this binary was built. No earlier release is ever applied on a build, so a new store build starts on its embedded bundle. | Create a release after the build. |
device_not_targeted | The release has --attribute or --device conditions this device does not match. A fresh install before login has set no attributes yet. | Set the attributes before the first sync, or release without the condition. See Target devices with attributes. |
release_revoked, bundle_failed_before | The running release was revoked, or the bundle failed its readiness gate on this device and is on its blocklist. | Publish a new release. clearUpdates() returns one device to its embedded bundle and clears its blocklist. See Roll back a release. |
build_debug | isEnabledInDebugBuilds is false in this build, or it is a React Native or Expo debug build, which embeds no bundle. | Test on a release build, or on a development build that follows its own channel. |
channel_unknown, or no index | setChannel({ name }) named a channel that is not discoverable, the build was made without a token or with HOTCODEPUSH_OFFLINE=1, or the channel the device follows was deleted or has expired: the device keeps its bundle and its channel and reports channel_unknown on every check. | Run npx hotcodepush channel update --channel beta --discoverable, switch by id as Switch channels by link shows, or rebuild with a token. A runtime choice on a channel that is gone is cleared with setChannel(null) or the debug screen's Return to configured channel, as Return to the build's channel shows. A recreated channel gets a new id, so only a runtime choice by name resolves again on its own; a choice by id needs setChannel with the new id, and a build whose channel is gone needs a new build. |
none, downloaded and nothing reloads | The app called setRestartAllowed({ isAllowed: false }) and never allowed restarts again. | Call setRestartAllowed({ isAllowed: true }) when the protected flow ends. The setting resets at every start. |
| none, every method throws "HotCodePush is not configured" | The build step never ran, so the resource file the SDK reads is missing. | npx hotcodepush doctor names the failed check. npx hotcodepush init repairs it, then build again. |
spending_cap_reached | The organization is at its spending cap. Devices beyond it receive nothing new this month. | Run npx hotcodepush organization update --spending-cap 200 as Owner, Admin or Billing. The change applies within about a minute. |
condition_unsupported, index_invalid | The SDK is older than a condition type or index format the release uses. Old SDKs fail closed, and the CLI warns at release time with the share of devices affected. | Ship a store build with a current SDK. Until then, release without the new condition type. |
device_offline, download_failed, connection_metered, content_mismatched, manifest_invalid, signature_invalid | The download did not finish or was refused, and every one retries on the next check. | device_offline and download_failed need nothing. connection_metered waits for Wi-Fi unless downloadStrategy is auto. A persistent signature_invalid is a key this build does not trust, see Sign your bundles. A persistent content_mismatched or manifest_invalid is a support request. |
| none, everything reads correct and nothing arrives | The release is not live yet. npx hotcodepush release get prints Live "not yet" while a change applies. | Wait a minute and probe again. If it still reads "not yet", send the probe's --json to support. |
If none of these fit
Send three things to support: the probe's --json output for the device, npx hotcodepush device get --device <id> --json, and the text the debug screen's share button copies. A device that can download updates but cannot report back updates normally and is missing from the console. The debug screen and the probe still work for it.
Last updated on
Supported versions
The React Native, Expo, Capacitor and Cordova versions each HotCodePush SDK supports, the minimum iOS and Android versions they set and how a version leaves support.
Results and reasons
Every status a HotCodePush sync returns, from up_to_date to skipped and failed, and every reason code with its meaning and what to do about it.