RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-05-29中級

Rork で『Network request failed』が Android エミュレータでだけ出るときの調べ方と直し方

iOS シミュレータでは通る fetch が、Android エミュレータでだけ『Network request failed』で落ちる現象の原因と、ローカル API・社内 API・本番 API を切り分けて直していく手順を、実機検証の順番までまとめます。

Rork515React Native209Android43Network2Troubleshooting

iOS シミュレータでは普通に取れているレスポンスが、Android エミュレータに切り替えた瞬間に TypeError: Network request failed で落ちる。Rork でアプリを書いていると、最初の社内 API 連携や、ローカルで立てた Node の開発サーバーに fetch() した直後に、必ず一度は通る道です。

私自身、2014年から個人でアプリを開発してきて、累計5,000万ダウンロード規模の壁紙・癒し・引き寄せ系アプリを運営する中で、何度も同じエラーに当たっています。原因は毎回違うのに、表示されるメッセージは同じです。だからこそ、最初に切り分け順を決めておくと、後で詰まったときに迷いません。同じ落とし穴に当たっている方の時間を少しでも縮められたらと思い、私が普段踏んでいる切り分けの順番を、現場の感覚そのままで残しておきます。

何が起きているのかを正しく言い直す

Network request failed は、ネットワーク層で何かが切れたときに React Native が投げる、かなり粒度の粗いエラーです。中身を読むと、だいたい次のどれかに当たります。

  1. リクエスト先のホストにそもそも到達できていない(DNS / IP / ポート)
  2. ホストには届いているが、TLS が成立していない(証明書 / 平文 HTTP の遮断)
  3. リクエストは届いて返ってきたが、Android の cleartext ポリシーで弾かれた
  4. プロキシ・VPN・社内ファイアウォール・Charles 等の中継で落ちた

iOS シミュレータと違ってつまずきやすいのは、Android エミュレータが「自分自身を Mac とは別の Linux 端末として動いている」という点です。ここを忘れると、対症療法を 30 分繰り返してしまいます。

まずやるべき切り分け:到達できているかどうか

エラーが起きた fetch のターゲットを、Android エミュレータ内のブラウザでまず開きます。Chrome を起動して URL を直接入力するだけです。

  • 開ければ「アプリ側の問題」(fetch のヘッダ・body・タイムアウト・モジュールの読み込み順)
  • 開けなければ「ネットワーク到達性の問題」(次節の localhost 周り)

この一手間で、調査時間がだいたい半分になります。私はチーム外の方のサポートに入ったときも、最初にこの確認を一緒にやることをお願いしています。

原因1:localhost を直接叩いている

Android エミュレータから見た localhost127.0.0.1 は「エミュレータ自身」を指します。Mac で動いている開発サーバー(Node, Rails, FastAPI, Supabase ローカル等)には届きません。これが、Rork でローカル開発するときの定番の落とし穴です。

Android Emulator から Mac 側のホストに到達するには、以下のいずれかを使います。

// utils/apiBase.ts
import { Platform } from 'react-native';
 
const LOCAL_PORT = 8787;
 
export const getApiBase = () => {
  // 本番
  if (!__DEV__) return 'https://api.example.com';
 
  // 開発: プラットフォーム別に切り替える
  if (Platform.OS === 'android') {
    // Android Emulator が Mac の localhost を指す特別な IP
    return `http://10.0.2.2:${LOCAL_PORT}`;
  }
  // iOS シミュレータは Mac とネットワーク空間を共有するのでそのままでよい
  return `http://localhost:${LOCAL_PORT}`;
};

実機の Android で動かす場合は 10.0.2.2 も効きません。Mac の LAN 内 IP(例:192.168.1.21)を環境変数から渡して、Wi-Fi 経由で接続するか、adb reverse tcp:8787 tcp:8787 で USB ポートフォワードを張ります。

# 実機 Android を USB 接続したまま、Mac 側のポートをエミュレータ的に見せる
adb reverse tcp:8787 tcp:8787

adb reverse は USB を抜くと切れるので、CI で常用するのは避けて、ローカル検証専用に留めるのがお勧めです。

原因2:HTTP(平文)通信が Android で遮断されている

Android 9 以降、デフォルトで平文 HTTP は遮断されます。http://10.0.2.2:8787 のような開発用 URL でも例外ではなく、Network request failed で落ちます。

Rork(Expo)の場合は、app.json で開発時のみ cleartext を許可するのが楽です。

{
  "expo": {
    "android": {
      "usesCleartextTraffic": true
    }
  }
}

ただし、これを本番ビルドにそのまま入れて出すのは避けるべきです。AdMob・Firebase・自社 API が混在している環境だと、攻撃面が広がります。私のアプリでは、開発用の app config (app.dev.json) と本番用 (app.json) を分けて、cleartext は開発限定にしています。

より制御を効かせたい場合は、network security config を書き、許可するホストだけ列挙します。

<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">10.0.2.2</domain>
    <domain includeSubdomains="true">192.168.1.21</domain>
  </domain-config>
</network-security-config>

「とりあえず usesCleartextTraffic: true にする → リリース時に外し忘れる」は実際に踏みやすい事故なので、開発用フラグから外したら App Bundle で必ず確認します。

原因3:自己署名 / 期限切れの証明書

社内 API や、mkcert で立てた開発用 HTTPS を叩いている場合に多い症状です。iOS シミュレータでは Mac のキーチェーンを信頼してくれることがあり、http→https で落ち始めて気づきます。

Android エミュレータのトラストストアは Mac とは独立しているため、自己署名は基本的に信頼されません。短期的には次のどちらかになります。

  • 開発期間中は usesCleartextTraffic で HTTP にフォールバック
  • Android 側に CA を読ませる(adb push + network_security_config.xml でユーザー CA を許可)

長期的には、Cloudflare Tunnel や ngrok のような正規証明書を吐く中継を一枚噛ませて、Android からも素直に HTTPS で取れる状態にしておくと、後から本番 API に差し替えるときに揉めません。

原因4:プロキシ・VPN・Charles の中継で落ちている

Charles Proxy を起動したまま、CA をインストールしていない状態で fetch() すると、Android では即 Network request failed です。原因として一番見落としやすいのがこれで、画面共有しながら一緒に調べると「Charles 立てっぱなしでした…」というケースに毎回出会います。

確認の順番は次の通りです。

  1. システム環境設定の Wi-Fi → プロキシ設定を確認
  2. Charles / Proxyman を一度終了して再試行
  3. 会社 VPN を切って再試行
  4. それでも落ちるなら Chrome で直接開く(前述)

私は Crashlytics + Remote Config で AdMob の頻度を絞る検証中、自分の Charles 経由で AdMob のリクエストが落ちて「夜だけ広告が出ない」と勘違いした経験があります。原因がアプリ内ではなく中継だと気づくまでに半日溶かしたので、ここはまず外して試すことを徹底しています。

アプリ側で見るべき2つの実装ミス

到達性が問題ない場合は、最後にコード側を見ます。Android で落ちて iOS で通る、というケースで頻出するのが次の2つです。

Content-Type を抜いて FormData を送っている

// ❌ Android だと境界文字列が付かず Network request failed になる
await fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'multipart/form-data' }, // ← 自分で指定してはいけない
  body: formData,
});
 
// ✅ Content-Type は React Native に任せる
await fetch(url, {
  method: 'POST',
  body: formData,
});

multipart/form-data の boundary はランタイムが自動付与します。iOS は寛容に処理してくれることがあるのに対して、Android は boundary が欠けた瞬間に切ります。

タイムアウトを設けていない fetch

Android エミュレータが裏でディスクスワップしている、Wi-Fi が一瞬切れた、といった状況で、fetch() は無限に待ち続けることがあります。AbortController で 10〜15 秒を上限に切るだけで、ユーザー体験も調査もぐっと楽になります。

const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 12_000);
try {
  const res = await fetch(url, { signal: ctrl.signal });
  return await res.json();
} catch (e: any) {
  if (e.name === 'AbortError') throw new Error('timeout');
  throw e;
} finally {
  clearTimeout(timer);
}

次のアクション

エラーが出たときの順番として、まずは「Android エミュレータの Chrome で開けるか」を 30 秒で確認するところから始めてください。そこから、localhost / cleartext / 証明書 / プロキシ の4つを順番に潰していけば、ほとんどのケースで原因まで辿り着けます。

私自身まだ学びの途中ですが、同じ場面に当たっている方の参考になれば嬉しいです。お読みいただき、ありがとうございました。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-05-19
Rork で AsyncStorage に大容量データを保存すると Android で CursorWindow エラーが出る原因と対処
Rork(React Native)で AsyncStorage に大きな JSON や画像メタデータを保存すると Android で発生する Row too big to fit into CursorWindow エラーの原因と、MMKV・SQLite・分割保存・圧縮など複数の現実的な対処をまとめています。
開発ツール2026-05-02
Rork で StatusBar の色・スタイルが反映されない時の対処法
Rork で生成したアプリの StatusBar が iOS と Android で意図通りに表示されない問題を、実装パターン別に切り分けて解決する手順をまとめました。
開発ツール2026-04-24
Rork アプリの WebView が真っ白になる・ページが表示されないときに確認する 6 つの原因
Rork で WebView を組み込んだら画面が真っ白になる、ページが読み込まれない、JavaScript が動かない ─ そんな症状を切り分けて確実に直すためのチェックリストです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →