RORK LABJP
PLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updatesPLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updates
Articles/Dev Tools
Dev Tools/2026-05-23Intermediate

Rork-Specific 'expo start --offline forbidden': Four Causes in Rork's Template Config

When expo start --offline returns 'forbidden' specifically on a Rork-generated project, the cause is usually Rork template config: tsconfigPaths, an un-generated expo-router cache, native prebuild, or a lockfile mismatch. Four Rork-specific fixes; the generic Expo proxy and dependency-validation guide is covered separately.

rork58expo11expo-offlinetroubleshooting66react-native12

You hop on a plane, want to keep iterating on the Rork prototype you started yesterday, type EXPO_OFFLINE=1 expo start --offline — and the CLI dies with Setting EXPO_NO_DEPENDENCY_VALIDATION or skipping validation via flags is forbidden in this environment. I'm Masaki Hirokawa, an indie developer running an iOS / Android app business since 2014 (collectively over 50 million downloads). Lately I've been using Rork for fast prototypes and testing them in offline-only environments — planes, exhibition halls, low-bandwidth trips — which is exactly where this error keeps showing up.

It's the collision between Expo CLI's safety net and Rork's default template configuration. Simply dropping --offline is not the fix. Triaged in the right order, every case has a clean path through.

What the error actually is — env vars and flags fighting for priority

Since Expo CLI 53, expo start validates that installed package versions match the SDK's expectations. Useful in normal use; awkward when you combine flags to go fully offline:

# The combination that triggers "forbidden" on Rork projects
EXPO_OFFLINE=1 EXPO_NO_DEPENDENCY_VALIDATION=1 expo start --offline

The CLI maintains an internal list of "environments where skipping validation must not be allowed." When Rork's generated app.json matches certain shapes (more below), the CLI refuses your implicit or explicit attempt to skip validation and aborts with forbidden. You only wanted "no network," but the CLI heard "also turn off the safety check," and said no.

First move: see where 'forbidden' actually fires

Same wording, multiple sources. The first thing to do is re-run with verbose logs and look at the lines immediately before the failure.

# Verbose logs, captured to a file for inspection
EXPO_DEBUG=1 EXPO_OFFLINE=1 expo start --offline --verbose 2>&1 | tee /tmp/expo-start.log
grep -B5 'forbidden' /tmp/expo-start.log | tail -20

In my experience the last few lines before forbidden always mention either validateDependenciesVersions (internal auto-skip path) or EXPO_NO_DEPENDENCY_VALIDATION (you set it explicitly). That distinction tells you which cause you're chasing.

Cause 1: Rork's expo.experiments.tsconfigPaths is on

Recent Rork templates set expo.experiments.tsconfigPaths: true in app.json. When that's on, Expo CLI refuses to auto-skip validation during --offline because TypeScript path resolution depends on it.

// app.json — typical shape generated by Rork
{
  "expo": {
    "name": "MyRorkApp",
    "experiments": {
      "tsconfigPaths": true,
      "typedRoutes": true
    },
    "plugins": [
      "expo-router",
      "expo-font"
    ]
  }
}

Two paths through this: drop --offline and use expo start --no-dev --minify instead, or temporarily flip those experiments off for the offline session. For venue-side emergency starts I keep this script around:

#!/usr/bin/env bash
# scripts/expo-offline-safe.sh — start a Rork project safely offline
set -euo pipefail
 
cp app.json app.json.backup
 
# Temporarily disable tsconfigPaths and typedRoutes
node -e "
  const fs = require('fs');
  const j = JSON.parse(fs.readFileSync('app.json', 'utf8'));
  if (j.expo && j.expo.experiments) {
    j.expo.experiments.tsconfigPaths = false;
    j.expo.experiments.typedRoutes = false;
  }
  fs.writeFileSync('app.json', JSON.stringify(j, null, 2));
"
 
trap "mv app.json.backup app.json" EXIT
 
EXPO_OFFLINE=1 expo start --offline

The trap that restores app.json on exit is the important bit — without it you'll forget you turned the experiments off and spend tomorrow wondering why typed routes broke.

Cause 2: expo-router cache hasn't been generated yet

A fresh-clone Rork project using Expo Router won't have .expo/types/router.d.ts yet. On first offline start, the CLI declares "I need online access to generate this cache" and returns forbidden.

Run one online start to materialize the cache, then go offline:

# Run online once to generate the cache (Ctrl-C after 10–30s is fine)
expo start --tunnel
# Verify the cache exists
ls -la .expo/types/
# Now offline works
EXPO_OFFLINE=1 expo start --offline

If .expo/ is in your .gitignore, every fresh clone needs this one-time online warmup.

Cause 3: Native prebuild hasn't run

If the project pulls in native modules like expo-haptics or expo-image and you haven't run npx expo prebuild, validateDependenciesVersions tries to compare against a non-existent native version and falls over.

Spot it: /tmp/expo-start.log mentions ios/ or android/. If yes, prebuild first:

# Prebuild without installing, then restart
npx expo prebuild --no-install
EXPO_OFFLINE=1 expo start --offline

--no-install is intentional — you don't want pod install or Gradle sync attempting network operations while offline. The prebuild alone gives the validator something to compare against, and forbidden goes away.

Cause 4: Lockfile drift in pnpm / bun

On Rork projects using pnpm or bun, a package.json that has drifted from its lockfile makes the CLI distrust the resolved versions and respond with forbidden.

# Verify lockfile is in sync with package.json
pnpm install --frozen-lockfile  # for pnpm
bun install --frozen-lockfile   # for bun

If --frozen-lockfile fails, the lockfile is stale. Run a normal install online to refresh it, then retry the offline start.

Triage order

Any of the four can fire alone or together. The order I run:

First, look at the EXPO_DEBUG=1 log for tsconfigPaths — that's cause 1. If absent, check .expo/types/ for the router cache — cause 2. Then check whether ios/ and android/ directories exist — cause 3. Finally verify lockfile sync — cause 4.

Following that order avoids unnecessary prebuilds and tunnel restarts, and I can usually resolve forbidden in under five minutes even on flaky travel Wi-Fi.

A durable fix: package.json scripts

I don't trust myself to remember these commands, so I keep them in package.json:

{
  "scripts": {
    "start": "expo start",
    "start:online-cache": "expo start --tunnel",
    "start:offline-safe": "bash scripts/expo-offline-safe.sh",
    "rebuild:native": "npx expo prebuild --clean --no-install",
    "lock:sync": "pnpm install --frozen-lockfile || pnpm install"
  }
}

The pattern on a freshly cloned Rork project: npm run start:online-cache for 30 seconds to warm caches, then npm run start:offline-safe for everything else. With this combo I've had zero offline startup failures across the last several exhibition trips, even when the venue Wi-Fi was unreliable.

Stay aware of Rork's template defaults

The four causes above are general Expo knowledge, but Rork's template defaults change over time. The expo.experiments block sometimes ships with tsconfigPaths and typedRoutes set to true and sometimes to false, depending on when you generated the project. That single difference flips how prone you are to forbidden.

When you scaffold a new Rork project, eyeball app.jsonexpo.experiments first and decide whether it lines up with your offline plans. I recommend making that a habit — in my experience it's the single check that cuts offline-side troubleshooting time the most.

What to try next

Once offline startup is stable, the next two things worth checking are Hot Reload behavior offline and Metro's bundler cache management. Knowing when to clear .expo/cache/ keeps long-trip development calm. Thanks for reading.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-03-26
Rork React Native Build Errors: A Diagnostic Path That Holds Up
When a Rork-generated React Native build won't go through, where do you look first? A diagnostic path that runs from Metro Bundler through native module linking, Gradle, Xcode, and Expo config — symptom to root cause.
Dev Tools2026-05-24
Why your Rork app shows a blank screen or loses state after returning from background
Your Rork app sits in the background overnight, you tap the icon the next morning, and the screen is blank — or your drafts have disappeared, or you have been silently logged out. iOS process reclamation, AsyncStorage rehydration timing, and Navigation state restoration each cause a different version of this bug. Notes from running wallpaper and wellness apps with 50M downloads as an indie developer.
Dev Tools2026-05-20
Fixing 0x8badf00d Watchdog Kills That Wipe Out Rork Apps at Launch
Your Rork iOS app dies right after launch on real devices, but never on your bench. Crashlytics shows exception code 0x8badf00d. Here is how to read the exact watchdog budget out of the crash log and rearrange your launch path so it stops.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →