> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.tester.army/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.tester.army/_mcp/server.

# Dev Builds & Metro

React Native and Expo apps built in **development mode** do not embed their JavaScript bundle. Instead, they load it at runtime from a [Metro](https://metrobundler.dev/) dev server running on your machine. TesterArmy test devices run in the cloud and cannot reach your local Metro server, so a dev-mode app cannot start.

## How this failure looks

When a dev-mode build is uploaded, the app launches but immediately shows one of these screens instead of your app:

* A red error screen with **"No script URL provided"**
* **"Could not connect to development server"**
* **"Unable to load script. Make sure you're either running Metro..."**
* The **Expo dev launcher** asking for a development server URL

When the agent detects this, the run fails with the step error code `MOBILE_RELEASE_BUILD_REQUIRED` and links to this page.

## Why it happens

Development builds (`Debug` configuration on iOS, `debug` variant on Android, and Expo builds with `expo-dev-client` / `developmentClient: true`) expect a Metro dev server to serve the JavaScript bundle over the network. That works on your machine and simulator, but not on a remote test device.

TesterArmy needs a **self-contained build** with the JavaScript bundle embedded in the app binary.

## How to fix it

Build a release artifact and upload that instead.

Build a **Release** iOS Simulator app. For Expo / React Native projects, generate native files first if needed:

```bash
npx expo prebuild --platform ios

xcodebuild -workspace ios/MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -sdk iphonesimulator \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath ios/build \
  build
```

The `-configuration Release` flag is what embeds the JavaScript bundle. The simulator app bundle ends up under:

```bash
ios/build/Build/Products/Release-iphonesimulator/MyApp.app
```

Build a **release** APK:

```bash
npx expo prebuild --platform android

cd android
./gradlew assembleRelease
```

**Debug APKs usually require Metro**

By default, React Native only bundles JavaScript into release variants. A
`./gradlew assembleDebug` APK works only if your build embeds the bundle
for debug builds too. When in doubt, upload the release APK.

Release builds must be signed. If you have not configured a release keystore, the default Expo prebuild setup signs release builds with the debug keystore, which is fine for testing.

## Expo EAS builds

If you build with EAS, make sure the build profile you upload from does **not** use a development client:

```json
{
  "build": {
    "testerarmy-ios-simulator": {
      "ios": { "simulator": true }
    },
    "testerarmy-android-apk": {
      "android": { "buildType": "apk" }
    }
  }
}
```

Avoid `"developmentClient": true` in the profile used for TesterArmy uploads — it produces an `expo-dev-client` build that opens the dev launcher instead of your app.

See [Expo EAS](/mobile/expo-eas) for the full CI workflow.

## Verify before uploading

To check a build locally, install it on a simulator/emulator **without Metro running** (do not run `npx expo start` or `npm start`) and launch it. If the app opens normally, it is self-contained and ready to upload. See [App Uploads](/mobile/app-uploads) for upload instructions.