How to Use This Page
Most Bundle Drop issues fall into one of a few buckets: setup, uploading, delivery, or crash protection. Find the symptom below, confirm the cause, and apply the fix.
Setup and Build
Bare Metro can't resolve bundle-drop-config
Your metro.config.js may be missing the Bundle Drop wrapper, or a custom export may bypass it. Wrap your final existing config so Bundle Drop can prepare the runtime configuration as well as the resolver:
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const { withBundleDrop } = require("@gfean/react-native-bundle-drop/metro");
// Preserve your app's existing Metro customizations here.
const config = mergeConfig(getDefaultConfig(__dirname), {});
module.exports = withBundleDrop(config, { projectRoot: __dirname });Rerun npx bundle-drop init --project-type bare, review the proposed Metro change, and then run npx bundle-drop doctor. Expo projects should preserve expo/metro-config through the Bundle Drop wrapper; see Expo.
Bundling fails with EMFILE
The OS file-descriptor limit is too low for the Metro bundle. Raise it and retry:
ulimit -n 8192
npx bundle-drop upload android --version 1.2.3 --channel developUploading
"Channel not found"
The channel you passed to --channel does not exist in the project. Channels are created in the dashboard — upload never auto-creates them. New projects start with a channel named develop. Check the channel name (case matters) or create it first. --channel is always required. See Channels.
"bundle-manifest.json is missing runtimeVersion"
The runtime version was not resolved during bundling. Check the selected compatibility mode in bundle.drop.config.js: manual mode needs a platform value, while fingerprint mode must successfully calculate the native identity. There is no --runtimeVersion flag — the value comes from the config-derived manifest. See Runtime Version.
"This bundle already exists — promote it instead"
You uploaded a bundle that is byte-for-byte identical to one already in the project. The error response (and the dashboard) shows which channels the bundle already lives on. Instead of re-uploading, promote the existing bundle to the target channel from its Bundles page. See Channels.
Authentication fails in CI
CI does not have an interactive bundle-drop login session. Pass a Personal Access Token with --token and keep it in your CI secret store. See CI/CD.
Update Not Delivered
When checkForUpdate() returns NOOP (or the app just never updates), work through these in order:
- Runtime version mismatch. Bundle Drop only delivers a bundle on the same runtime version as the installed binary. On a channel without managed publishing, a mismatch surfaces as a
NO_COMPATIBLE_BUNDLEreason carryingrequestedRuntimeVersionandlatestRuntimeVersionOnChannel; on a managed-publishing channel it simply looks like no eligible bundle. See Runtime Version. - Managed publishing with no active rollout. If the channel has Managed Publishing enabled, only bundles in an active or completed rollout are eligible — a paused rollout makes its bundle ineligible too. Start, resume, or advance a rollout for the bundle.
- Rollout percentage excludes this install. Staged rollouts are bucketed deterministically per install, so a device below the current percentage will not receive the update until you raise it. See Staged Rollouts.
- Targeting mismatch. If the rollout has targeting rules, the device's user properties must match at least one rule. Confirm the properties are set in app code and defined in the dashboard. See Targeted Rollout.
- Wrong channel. Confirm the app's active
channelNamematches the channel you uploaded to.
Update downloaded but not applied
With policy: "on-next-launch", the bundle is staged and applied on the next launch — this is expected. Restart the app, or call applyUpdate() yourself. Check getUpdateState() for pendingApply. See Update Policies.
Fingerprint Mismatch
Inspect the current platform with npx bundle-drop fingerprint --platform ios or --platform android, and compare a saved report when available. Check changes to native dependencies, configuration, lockfiles, and any explicit platform runtime boundary. A tooling or JavaScript change inside an autolinked native dependency can also change its package digest.
If the source legitimately belongs to a new runtime, build that native binary before expecting delivery. If the upload should match an existing binary, restore the matching inputs rather than overriding its hash. Changing a config file or running an inspection command cannot rewrite the identity inside an installed app. See Native Fingerprinting.
Also verify declared build variants and explicit sources. A custom native change outside collector coverage may need a source declaration or a deliberate hybrid boundary change, even if the existing hash did not change. Avoid weakening ignore rules just to make mismatched inputs appear compatible.
Crash Protection and Rollback
An update was rolled back automatically
The bundle repeatedly launched without being marked healthy, so on-device startup protection recovered to a healthy fallback. The failed bundle is blocked on that device and reported as skippedFailedBundle on later checks. Publish a new bundle to ship a fix — do not retry the same failed update on that device. See Automatic Recovery.
A healthy update keeps rolling back
You are likely in manual health mode (healthCheckMode: "manual") without calling reportHealthy(). Each later distinct launch then classifies the previous unfinished attempt. Call BundleDrop.reportHealthy() at your startup readiness point, or switch back to auto. See Manual Health Reporting.
Error Tracking
Stack traces from OTA bundles are not symbolicated
Source maps were not uploaded, or the dist/release identifier does not match the bundle. Upload with --sourcemap, and use the bundle's hash (from getObservabilityContext().dist) as the dist identifier in both CI and runtime config. See Observability.
Related Docs
- For setup, see Installation.
- For publishing, see Uploading.
- For delivery gating, see Managed Publishing and Staged Rollouts.
- For crash protection, see Rollback.
- For error tracking, see Observability.