You publish an EAS Update, scan the QR code from the dashboard with Expo Go on your iPhone, and the app opens. You scan the same QR code with Expo Go on an Android phone, and a red screen shows a single line before everything stops.
Uncaught Error: java.io.IOException: Failed to download remote update
"View error log" shows that same line and nothing else: no HTTP status, no name for the asset that failed. You rebuild the update, clear caches, reinstall Expo Go, and end up back on the same screen. If that's your evening right now, here is what has been reported about the cause, and the order I'd check things in.
Draw one line first: suspect Expo Go, not the update
I ship wallpaper apps on both iOS and Android, so when only one platform behaves differently my first instinct is to blame my own configuration. This bug punishes that instinct.
There is a report on the Expo repository that matches this exactly (expo/expo#50139). It uses a blank create-expo-app project on SDK 57 with only expo-updates added. The same update group opened on iOS and failed on two Android phones. Five dependencies, one asset; there is almost nothing in the project to suspect.
So I'd check these four things, in this order.
| What to check | What the answer tells you |
|---|---|
| Does the same update open in Expo Go on iOS? | If yes, the update itself is healthy. Look at the Android-only path. |
| Is the Android device real or an emulator? | If it opens on the emulator and fails only on a real phone, you are likely in this bug. |
| Which Expo Go version? | 57.0.9, the build for SDK 57, is where it was reported. The 58 line is a different story, below. |
| What HTTP status does the server return? | 403 means you are turned away before loading starts. 404 means the update group or runtime version is wrong. |
If the first two come back "yes", it is reasonable to stop suspecting assets and bundles and start suspecting what Expo Go puts in its request.
The cause: the sign-in token lived in two places
A second report (expo/expo#50253) traces it down to specific source lines.
After the Android home screen was rewritten, Expo Go started saving your sign-in secret to a store called expo_session. The code that loads an update kept reading an older store, expo_auth_session. The only thing that ever wrote to that older store was the previous home screen, which the rewrite removed.
So when you sign in from the profile tab, the home screen lists your projects and branches correctly. On screen, you are authenticated. But the requests that fetch the manifest and assets go out with no Expo-Session header.
According to the report, the server answers with this 403:
403: Experience "@owner/slug" is not viewable in Expo Go: this project requires authentication.
Sign in to Expo Go with an Expo account that is a member of the account that owns this project.
The reporter says that even after signing in and reopening the same link, the same failure came back in about 0.6 seconds. The fix PR (expo/expo#50498) also explains why this stayed hidden for so long: the server only recently began requiring ownership to load updates in Expo Go, and emulators identify themselves as EXPO_SIMULATOR, which the server doesn't gate. That matches "works on the emulator, fails on the real phone".
Being signed in on screen and loading with the same key are two different things.
Two things to run yourself
The first is a small script that asks the server whether the update group is visible per platform, without Expo Go in the way. It sends the same kind of request with curl.
#!/usr/bin/env bash
# Usage: ./manifest_probe.sh <projectId> <groupId> <SDK major number>
set -euo pipefail
PID="${1:?projectId}"; GID="${2:?groupId}"; SDK="${3:?sdk major}"
URL="https://u.expo.dev/${PID}/group/${GID}"
for P in ios android; do
CODE=$(curl -sS -m 20 -o "/tmp/probe_${P}.txt" -w '%{http_code}' "$URL" \
-H "expo-platform: ${P}" \
-H "expo-runtime-version: exposdk:${SDK}.0.0" \
-H "accept: multipart/mixed")
echo "${P}: HTTP ${CODE}"; head -c 300 "/tmp/probe_${P}.txt"; echo
doneWhen I ran it with IDs that don't exist, this is what came back.
ios: HTTP 404
There is no update identified by group ID "11111111-1111-4111-8111-111111111111" and platform "ios"
android: HTTP 404
There is no update identified by group ID "11111111-1111-4111-8111-111111111111" and platform "android"
Reading it is simple. If both are 200, the update exists on the server for both platforms. If only one is 404, the eas update for that platform didn't go through. If both are fine and only Expo Go on Android fails, the problem is in what Expo Go sends, not in your update. Note that this script carries no Expo Go sign-in, so it won't reproduce the 403. Its job is to rule out a missing group or a mismatched runtime version first.
The second is for a real Android phone: read the Expo Go version and pull the failure line.
# Check the Expo Go version, then pull only the load-failure line
adb shell dumpsys package host.exp.exponent | grep versionName
adb logcat -d | grep -i "Remote update request not successful"If that line is in the log, the server received the request and answered with something other than 2xx. That is the fact the one-line error screen leaves out. I haven't run these two commands, because I have no Android phone connected here; I wrote them from the description in the report.
When does it get fixed? Only what I could confirm
The fix PR was merged into main on 2026-09-23, which I read from the PR's state. I then compared the actual source on the two branches.
| Branch | Where the update request reads the session from |
|---|---|
| sdk-57 | Still the old store (`exponentSharedPreferences.sessionSecret`) |
| sdk-58 | Changed to read the new store (`sessionRepository`) |
The last Expo Go for SDK 57 is 57.0.9 (published 2026-08-15), and I couldn't find a newer 57 build. So with SDK 57 and Expo Go on Android, opening an update that requires ownership looks unfixed for now. Expo Go on the 58 line (58.0.7 was published on 2026-10-24) contains the fix in source. I haven't confirmed behavior on a real phone with the store build of 58, and I'd rather say so than imply it.
The ways around it either skip the Expo Go loader or change which path you take.
- Check on Expo Go for iOS. In the report, the same update opens there.
- Check on an Android emulator. Per the PR, the server doesn't gate emulators.
- Apply the update to an app you built yourself (a development build or an internal-distribution build) instead of Expo Go. The bug is inside Expo Go's update loader, so that path doesn't go through it.
- Move to SDK 58 and test with Expo Go for 58. The migration is large, so I wouldn't do it just for this.
Which one fits depends on your situation. Just knowing that the update isn't broken is enough to stop the rebuild-and-republish loop.
If you want to sort through the other ways an update fails to show up, there is five typical patterns when an EAS Update doesn't appear. The order for checking the Expo Go sign-in itself is in the two logins to check first when Expo Go stops opening your preview.
Next time only Android disagrees with iOS, try these two checks before you rebuild anything: does it open on iOS, and is the Android device real or an emulator? I'm putting those two at the top of my own checklist for platform-only differences.