You sit down in a weak-Wi-Fi cafe, fire expo start --offline, and Expo refuses:
validateDependenciesVersions cannot be disabled in this environment
Or you try EXPO_OFFLINE=1 expo start and it comes back with forbidden. The whole point of offline mode is to skip the npm registry round-trip, and the thing blocking it is a dependency-version check that needs… the registry. It is a small loop of frustration.
As an indie developer running an app business since 2014, with wallpaper apps that have crossed 50M cumulative downloads, I now use Rork in new projects layered onto that older app business — and I have hit this in airports, hotels, and onsite while preparing exhibitions abroad. The error has three intertwined causes, not one. Here is the diagnostic order that has reliably gotten me back to coding.
First, which forbidden is this — a proxy 403 or the CLI guard?
The same word forbidden covers two completely different failures, and telling them apart first saves you from applying the wrong fix.
- An HTTP 403 from a corporate proxy or firewall. On a company or campus network, the proxy can reject the signed-manifest and version-check requests
expo startmakes in the background, returning403 Forbidden. If the message includesrequest failed,403,ECONNREFUSED, or your proxy hostname, this is your case. - Expo CLI own guard. If it spells out
Setting EXPO_NO_DEPENDENCY_VALIDATION ... is forbidden in this environment, that is thevalidateDependenciesVersionsstory covered below.
Get the detailed log first:
EXPO_DEBUG=1 expo start --offline --verbose 2>&1 | tee /tmp/expo-start.log
grep -iE "403|forbidden|proxy|ECONN" /tmp/expo-start.logIf it is a proxy 403: route through the proxy, or go fully offline
You have two clean options.
A. Stay online, but go through the proxy. Expo CLI reads the HTTP_PROXY / HTTPS_PROXY environment variables and sends every network request through that proxy (internally via Undici EnvHttpProxyAgent). Point npm at the same proxy so installs do not stall either.
export HTTP_PROXY="http://user:pass@proxy.example.com:8080"
export HTTPS_PROXY="http://user:pass@proxy.example.com:8080"
npm config set proxy "http://user:pass@proxy.example.com:8080"
npm config set https-proxy "http://user:pass@proxy.example.com:8080"
expo startB. Never touch the network at all. If the CLI makes no requests, the proxy cannot 403 you. EXPO_OFFLINE=1 (or --offline) stops Expo from making network calls, including manifest signing and version checks — but generate the first-run caches once on an unrestricted network beforehand (see the pre-caching section below).
EXPO_OFFLINE=1 expo start --offlineUse A for day-to-day work on the corporate network, B for planes and venues where the proxy is not reachable. --tunnel cannot run offline, so it is not the answer here.
If clearing the proxy 403 still leaves a separate validateDependenciesVersions cannot be disabled, that is the CLI guard — work through the three knobs next.
The three knobs that decide whether offline actually works
This is rarely a single-cause failure. It happens when all three of the following are wrong at once:
- Expo CLI version is on SDK 50 or later — before 50, offline mode bypassed this validation; after 50 it became mandatory by default
- No
install.excludeblock inpackage.json'sexpofield — there are packages in conflict and none are excused EXPO_OFFLINEis not actually reachingprocess.env— usually a shell quoting or shell-type issue
I have hit all three at the same time during one trip and lost a full afternoon to it.
1. Confirm your Expo CLI version
npx expo --version
# e.g., 0.18.21
cat package.json | grep '"expo":'
# e.g., "expo": "~50.0.6"If you are pre-SDK-50, the cause is probably something else (network reachability or a registry timeout) — fix differently.
2. Configure install.exclude
Add an expo.install.exclude block to package.json:
{
"name": "wallpaper-rork-app",
"version": "2.1.0",
"scripts": {
"start": "EXPO_OFFLINE=1 expo start --offline"
},
"dependencies": {
"expo": "~50.0.6",
"react-native": "0.73.4",
"react-native-reanimated": "~3.6.2",
"expo-image": "~1.10.6"
},
"expo": {
"install": {
"exclude": [
"react-native-reanimated",
"expo-image"
]
}
}
}List the specific packages that conflict, not every dependency. Reanimated and expo-image are the two that come up most often in the wallpaper apps I work on.
3. Make sure the env var actually arrives
Inline env vars are not portable across shells. Verify:
EXPO_OFFLINE=1 expo start --offline
env | grep EXPO_OFFLINE
# should print EXPO_OFFLINE=1Fish shell and Windows PowerShell do not accept the VAR=value command form. On PowerShell:
$env:EXPO_OFFLINE = "1"
npx expo start --offlineLong-term fix is cross-env in your package.json so the same script works on all three platforms:
{
"scripts": {
"start:offline": "EXPO_OFFLINE=1 expo start --offline",
"start:offline:win": "cross-env EXPO_OFFLINE=1 expo start --offline"
}
}4. Wipe the caches
Metro can carry stale validation rules across runs. After the above is set:
rm -rf node_modules/.cache/metro
rm -rf /tmp/metro-*
rm -rf node_modules/.cache/expo
EXPO_OFFLINE=1 expo start --offline --clearThe --clear flag wipes Metro's bundle cache at startup. Combined with EXPO_OFFLINE=1, this is what gets the validation flag to actually behave.
5. If it still says forbidden, the CLI itself may be the bug
I have seen Expo CLI 0.18.16 silently fail to honor the disable flag, with 0.18.21 fixing it days later. If you are stuck after all of the above:
npx expo@latest --version
npm install expo@latestThe pattern I've noticed: in the first 1–2 weeks after a minor SDK release (SDK 51.0.0 was a recent example), validation-flag bugs are more common, then they get fixed quickly.
Three real conflict examples from my project
For reference, three concrete dependency conflicts that have actually blocked me with validateDependenciesVersions:
First, react-native-reanimated against Expo SDK pinning. Right after SDK 50 dropped, Reanimated 3.6.2 was a hair off from SDK's recommended 3.6.0. Added Reanimated to install.exclude; it resolved by itself when 3.6.3 came out.
Second, expo-image vs. native Glide pinning. My ad SDK pins Glide for AdMob compatibility; expo-image wanted a newer Glide. Same fix — exclude expo-image.
Third, and the one that cost the most time: dev-client vs production SDK drift. My EAS-built dev-client was on SDK 50.0.6 while my local package.json had drifted to 50.0.10. install.exclude doesn't fix this — you have to realign your local package.json to the dev-client's exact version. I now keep a comment in .eas/build-profile.json reading "dev-client = expo@50.0.6" and a rule to update both together.
Pre-caching for travel
To stop hitting this on the road, I added an offline-prep step at home before any trip:
# At home, online
expo install --check
git stash
zip -r wallpaper-rork-app-cached.zip . -x ".git/*"On the trip, unzipping that bundle and running EXPO_OFFLINE=1 expo start --offline works fully offline, validation and all. I learned this the hard way during a week of exhibition prep abroad where the venue Wi-Fi was unusable — the pre-baked bundle is what saved that project.
Diagnostic checklist
The order I now run through before any trip:
- [ ]
npx expo --versionshows 0.18.21 or newer - [ ] Conflicting packages added to
expo.install.exclude - [ ]
env | grep EXPO_OFFLINEconfirms the variable is set - [ ]
cross-envis in place if I'll be on Windows - [ ] Cleared
node_modules/.cache/metroand ran with--clear - [ ] If still failing, Expo CLI bumped to the latest patch
In my own runs, this list resolves the validateDependenciesVersions blocker in ≥95% of attempts. The remaining few are usually a CLI-side patch issue that fixes itself within a week or two.
Offline mode is supposed to be the answer when networks are flaky. Watching the offline switch itself refuse to work is painful — I hope this saves someone else a flight's worth of debugging.