Troubleshooting
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 is missing the alias, or a custom config replaced the default resolver. Merge the alias into your existing config instead of overwriting it:
const path = require("path");
const { getDefaultConfig } = require("@react-native/metro-config");
const config = getDefaultConfig(__dirname);
config.resolver = config.resolver || {};
config.resolver.extraNodeModules = {
...(config.resolver.extraNodeModules || {}),
"bundle-drop-config": path.resolve(__dirname, "bundle.drop.config.js"),
};
module.exports = config;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. Set it in bundle.drop.config.js per platform and re-run the upload. 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.
Crash Protection and Rollback
An update was rolled back automatically
The bundle repeatedly launched without being marked healthy, so on-device crash protection reverted it. 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 Rollback.
A healthy update keeps rolling back
You are likely in manual health mode (healthCheckMode: "manual") without calling reportHealthy(), so every launch counts as unconfirmed. Either call BundleDrop.reportHealthy() at your startup readiness point, or switch back to auto. See Rollback.
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.
