1--- 2title: Advanced EAS Update Debugging 3sidebar_title: Advanced 4description: Learn advanced strategies on how to debug EAS Update. 5--- 6 7import ImageSpotlight from '~/components/plugins/ImageSpotlight'; 8import { Terminal } from '~/ui/components/Snippet'; 9 10After verifying our EAS Update configuration in our [basic guide](/eas-update/debug), we can move on to more advanced debugging strategies. The following sections describe common classes of problems and strategies on how to tackle them. 11 12## General strategies 13 14Try these strategies before using the more specific ones mentioned in this guide. 15 16### Use `expo-dev-client` 17 18Create a [development version of our build](/eas-update/expo-dev-client). It will help us preview published updates inside a problematic build. 19 20### In-app debugging 21 22The `expo-updates` library exports a variety of functions to interact with updates once the app is already running. In certain cases, making a call to fetch an update and seeing an error message can help us narrow down the root cause. We can make a simulator build of the project and manually check to see if updates are available or if there are errors when fetching updates. 23 24- Print the [Update.Constants](/versions/latest/sdk/updates/#constants) to verify our configuration. 25- [Examine log entries](/versions/latest/sdk/updates/#updatesreadlogentriesasyncmaxage) surfaced from the native layer. 26- Fetch and [load updates manually](/versions/latest/sdk/updates/#check-for-updates-manually). 27 28## Configuration issues 29 30Our app is still not receiving the expected update despite following the [basic guide](/eas-update/debug). 31 32### `expo-updates` configuration 33 34The `expo-updates` library runs inside an end-user's app and makes requests to an update server to get the latest update. 35 36#### Verifying app configuration 37 38When we set up EAS Update, we likely ran `eas update:configure` to configure expo-updates to work with EAS Update. This command makes changes to our app config (**app.json**/**app.config.js**). Here are the fields we'd expect to see: 39 40- `runtimeVersion` should be set. By default, it is `{ "policy": "sdkVersion" }`. If our project has **android** and **ios** directories, we'll have to set the `runtimeVersion` manually. 41- `updates.url` should be a value like `https://u.expo.dev/your-project-id`, where `your-project-id` matches the ID of our project. We can see this ID on [our website](https://expo.dev/accounts/[account]/projects/[project]). 42- `updates.enabled` should not be `false`. It's `true` by default if it is not specified. 43 44Finally, make sure that `expo-updates` is included in **package.json**. If it's not, run: 45 46<Terminal cmd={['$ npx expo install expo-updates']} /> 47 48#### Inspecting expo-updates configuration after prebuild 49 50Whenever we run `eas build`, the `npx expo prebuild` command is run on our project on EAS' servers to unpack the **android** and **ios** directories that contain native files. This makes it so EAS Build can build any project, whether it includes the native files or not. 51 52If our project does not have **android** or **ios** directories, we can make commit any existing changes, then run `npx expo prebuild` to inspect the project state that EAS Build will act on. After running this, look for the following files: **android/app/src/main/AndroidManifest.xml** and **ios/your-project-name/Supporting/Expo.plist**. 53 54In each, we expect to see configuration for the EAS Update URL and the runtime version. Here are the properties we'd expect to see in each file: 55 56**AndroidManifest.xml** 57 58```xml 59... 60<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="your-runtime-version-here"/> 61<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/your-project-id-here"/> 62... 63``` 64 65**Expo.plist** 66 67```xml 68... 69<key>EXUpdatesRuntimeVersion</key> 70<string>your-runtime-version-here</string> 71<key>EXUpdatesURL</key> 72<string>https://u.expo.dev/your-project-id-here</string> 73... 74``` 75 76### Inspecting a build manually 77 78When building a project into an app, there can be multiple steps that alter the output of `npx expo prebuild`. After making a build, it is possible to open the build's contents and inspect native files to see its final configuration. 79 80Here are the steps for inspecting an iOS Simulator build on macOS: 81 821. Create an iOS Simulator build of the app using EAS Build. This is done by adding `"ios": { "simulator": true }` to a build profile. 832. Once the build is finished, download the result and unzip it. 843. Then, right click on the app and select "Show Package Contents". 854. From there, we can inspect the **Expo.plist** file. 86 87Inside the **Expo.plist** file, we expect to see the following configurations: 88 89```xml 90... 91<key>EXUpdatesRequestHeaders</key> 92<dict> 93 <key>expo-channel-name</key> 94 <string>your-channel-name</string> 95</dict> 96<key>EXUpdatesRuntimeVersion</key> 97<string>your-runtime-version</string> 98<key>EXUpdatesURL</key> 99<string>https://u.expo.dev/your-project-id</string> 100... 101``` 102 103### Inspecting manifests manually 104 105When an update is published with EAS Update, we create a manifest that end-user app's request. The manifest has information like which assets and versions are needed for an update to load. We can inspect the manifest by going to a specific URL in a browser or by using `curl`. 106 107Inside our project's app config (**app.json**/**app.config.json**), the URL we can GET is under `updates.url`. 108 109This `url` is EAS' "https://u.expo.dev" domain, followed by the project's ID on EAS' servers. If we go to the URL directly, we'll see an error about missing a header. We can view a manifest by adding three query parameters to the URL: `runtime-version`, `channel-name`, and `platform`. If we published an update with a runtime version of `1.0.0`, a channel of `production` and a platform of `android`, the full URL we could visit would be similar to this: 110 111``` 112https://u.expo.dev/your-project-id?runtime-version=1.0.0&channel-name=production&platform=android 113``` 114 115### Viewing network requests 116 117Another way to identify the root cause of an issue is to look at the network requests that the app is making to EAS servers, then viewing the responses. We recommend using a program like [Proxyman](https://proxyman.io/) or [Charles Proxy](https://www.charlesproxy.com/) to watch network requests from our app. 118 119With either program, we'll need to follow their instructions for installing an SSL certificate, so that the program can decode HTTPS requests. Once that's set up in a simulator or on an actual device, we can open our app and watch requests. 120 121The requests we're interested in are from https://u.expo.dev and https://assets.eascdn.net. Responses from https://u.expo.dev will contain an update manifest, which specifies which assets the app will need to fetch to run the update. Responses from https://assets.eascdn.net will contain assets, like images, font files, etc that are required for the update to run. 122 123When inspecting the request to https://u.expo.dev, we can look for the following request headers: 124 125- `Expo-Runtime-Version`: this should make the runtime version we made our build and update with. 126- `expo-channel-name`: this should be the channel name specified in the **eas.json** build profile. 127- `Expo-Platform`: this should be either "android" or "ios". 128 129As for all requests, we expect to see either `200` response codes, or `304` if nothing has changed. 130 131Below is a screenshot showing the request of a successful update manifest request: 132 133<ImageSpotlight 134 alt="Successful manifest request" 135 src="/static/images/eas-update/network-request.png" 136/> 137 138## Runtime issues 139 140We are able to load the expected update but our project is displaying unexpected behavior. 141 142### Debugging of native code while loading the app through expo-updates 143 144By default, we need to make a release build for `expo-updates` to be enabled and to load updates rather than reading from a development server. This is because debug builds behave like normal React Native project debug builds. 145 146To make it easier to test and debug native code in an environment that is closer to production, follow the steps below to create a debug build of the app with `expo-updates` enabled. 147 148We also provide a [step-by-step guide to try out EAS Update quickly](/eas-update/build-locally) in a local development environment using Android Studio or Xcode, with either release or debug builds of the app. 149 150#### iOS local builds 151 152- Set the debug environment variable: `export EX_UPDATES_NATIVE_DEBUG=1` 153- Reinstall pods with `npx pod-install`. The `expo-updates` podspec now detects this environment variable, and makes changes so that the debug code that would normally load from the Metro packager is bypassed, and the app is built with the EXUpdates bundle and other dependencies needed to load updates from EAS. 154- [Ensure the desired channel is set in our **Expo.plist**](/bare/updating-your-app/#configuring-the-channel-manually) 155- Modify the application Xcode project file to force bundling of the application JavaScript for both release and debug builds: 156 157``` 158sed -i '' 's/SKIP_BUNDLING/FORCE_BUNDLING/g;' ios/<project name>.xcodeproj/project.pbxproj 159``` 160 161- Execute a [debug build](/debugging/runtime-issues/#native-debugging) of the app with Xcode or from the command line. 162 163#### Android local builds 164 165- Set the debug environment variable: `export EX_UPDATES_NATIVE_DEBUG=1` 166- [Ensure the desired channel is set in your **AndroidManifest.xml**](/bare/updating-your-app/#configuring-the-channel-manually) 167- Execute a [debug build](/debugging/runtime-issues/#native-debugging) of the app with Android Studio or from the command line. 168 169#### EAS Build 170 171Alternatively, we can use EAS to create a debug build where `expo-updates` is enabled. The environment variable is set in **eas.json**, as shown in the example below: 172 173```json eas.json 174{ 175 "build": { 176 "preview_debug": { 177 "env": { 178 "EX_UPDATES_NATIVE_DEBUG": "1" 179 }, 180 "android": { 181 "distribution": "internal", 182 "withoutCredentials": true, 183 "gradleCommand": ":app:assembleDebug" 184 }, 185 "ios": { 186 "simulator": true, 187 "buildConfiguration": "Debug" 188 }, 189 "channel": "preview_debug" 190 } 191 } 192} 193``` 194 195## Publishing issues 196 197We are not able to publish an update, or parts of our update are not being published as expected. 198 199### Inspecting the latest update locally 200 201When we publish an update with EAS Update, it creates a **/dist** folder in the root of our project locally, which includes the assets that were uploaded as a part of the update. 202 203<ImageSpotlight alt="Dist directory" src="/static/images/eas-update/dist.png" /> 204 205### Viewing all assets included in an update 206 207It may be helpful to see which assets are included in our update bundle. We can see a list of named assets by running: 208 209<Terminal cmd={['$ npx expo export']} /> 210 211## Mitigation steps 212 213Once we've found the root cause of the issue, there are various mitigation steps we might want to take. One of the most common problems is pushing an update that has a bug inside it. When this happens, we can re-publish a previous update to resolve the issue. 214 215### Re-publishing a previous update 216 217The fastest way to "undo" a bad publish is to re-publish a known good update. Imagine we have a branch with two updates: 218 219```bash 220branch: "production" 221updates: [ 222 update 2 (id: xyz2) "fixes typo" // bad update 223 update 1 (id: abc1) "updates color" // good update 224] 225``` 226 227If "update 2" turned out to be a bad update, we can re-publish "update 1" with a command like this: 228 229<Terminal 230 cmd={[ 231 '# eas update:republish --group [update-group-id]', 232 '', 233 '# eas update:republish --branch [branch-name]', 234 '', 235 '', 236 '# Example', 237 '$ eas update:republish --group abc1', 238 '$ eas update:republish --branch production', 239 ]} 240/> 241 242The example command above would result in a branch that now appears like this: 243 244```bash 245branch: "production" 246updates: [ 247 update 3 (id: def3) "updates color" // re-publish of update 1 (id: abc1) 248 update 2 (id: xyz2) "fixes typo" // bad update 249 update 1 (id: abc1) "updates color" // good update 250] 251``` 252 253Since "update 3" is now the most recent update on the "production" branch, all users who query for an update in the future will receive "update 3" instead of the bad update, "update 2". 254 255While this will prevent all new users from seeing the bad update, users who've already received the bad update will run it until they can download the latest update. Since mobile networks are not always able to download the most recent update, sometimes users may run a bad update for a long time. When viewing error logs for our app, it's normal to see a lingering long tail of errors as our users' apps get the most recent update or build. We'll know we solved the bug when we see the error rate decline dramatically; however, it likely will not disappear completely if we have a diverse user base across many locations and mobile networks. 256