こんにちは!Kredo Blog編集部のさおりです!
JavaScriptでデータを取ろうとしたら、Failed to fetch。直したつもりでも、今度はresponse.json()で止まる。そんなとき、表示された単語だけを検索し続けると、どこを直す話なのか分からなくなります。
先に確かめたいのは、応答を受け取れたか、HTTPとして成功しているか、本文を読めたかの3段階です。404でもJSONを読み取れる場合があり、逆に200でも本文の読み取りに失敗します。この記事では、その違いを自分のパソコンで比べられる9ケースの教材を使って説明します。
fetchのエラーは、止まった段階から調べる

fetch()が返すPromiseから得られるのは、応答を表すResponseです。本文そのものではありません。そこで取得処理と、reply.json()などで本文を読む処理を分けて考えます。
名前をreplyにしているだけで、よく見かけるresponseと同じ役割です。ステータスとヘッダーを得られた時点では、本文の受信がまだ終わっていないこともあります。まずは次の順で観察しましょう。MDN:Fetchの使用
| 確認する段階 | 見るもの | 次に調べる場所 |
|---|---|---|
| Responseを取得できたか | await fetch()の結果 |
要求先、通信、ブラウザが表示するエラー、中断の有無 |
| HTTPは成功側か | statusとok |
要求先のパス、対象データ、サーバーの応答 |
| 本文を読めたか | Content-Typeと本文を読む処理 | 本文の種類、空の本文、構文、受信や読み取りの状態 |
表は横にスクロールできます。
最後の段階まで進めばデータを取り出せます。ただし、その値に画面で使いたい項目があるかは別の確認です。JSONの構文とデータの形は、JSON.parseのエラーと値の検証を扱う記事で練習できます。
404・500がcatchに入らないときはHTTP状態を見る

「404なのだから、fetch()が自動で失敗するはず」と考えたくなります。しかし、HTTPのエラー応答を受け取れた場合、fetch()は通常そのResponseを返します。本文が正しいJSONなら、その後のjson()も読めてしまいます。
JSONを読めたことを、HTTPの成功と取り違えない
教材の404は、{"error":"Lesson item not found"}という本文を返します。下のコードは、後半の「ローカル教材」のサーバーを起動し、そのページの開発者ツールで実行する例です。HTTP状態を確認していないため、404でも最後の行まで進みます。
// 比較用:HTTP状態を確認していない例です。
const reply = await fetch('/lab/not-found');
const data = await reply.json();
console.log(reply.status, data.error);
// 404 'Lesson item not found'
errorというキーも、JSONに入っている名前の一つです。fetchがその名前を見て自動的に例外を投げるわけではありません。教材では500のJSON応答でも、同じ違いを確認できます。
okを調べ、必要なら自分で例外にする
Response.okは、ステータスが200〜299のときにtrueになります。HTTPの失敗を同じ例外処理で受けたいなら、!reply.okを確認してから自分で例外を投げます。次はその部分を示す小さな関数です。MDN:Response.ok
async function readLessonJSON(path) {
const reply = await fetch(path);
if (!reply.ok) {
throw new Error(`HTTP ${reply.status}`);
}
if (reply.status === 204) return null;
return await reply.json();
}
readLessonJSON('/lab/not-found')のPromiseは、ここでは自分で作ったError: HTTP 404で失敗します。一方、/lab/okなら値を返します。fetchが投げた例外なのか、自分のコードで投げた例外なのかも区別してください。この短い関数だけでは本文の種類や例外の段階までは整理していないため、後述の配布教材で確認を広げます。
200でも、本文をJSONとして読めるとは限らない

HTTPが成功側でも、想定した形式の本文とは限りません。教材には、200でHTMLを返すケースと、200で壊れたJSONを返すケースを別々に用意しました。同じ「JSONとして読めない」でも、最初に見る場所が異なります。
HTMLなら、要求先と応答の種類を確かめる
Content-Typeがtext/htmlなら、まずJSONを期待した要求先で合っているかを見ます。実サービスではログイン画面などが返る可能性もありますが、HTMLというだけで理由を決めつけません。教材の/lab/htmlは、この違いを見るために意図的にHTMLを返しています。
本文を確認するときは、次のようにテキストとして一度だけ読みます。実サービスの本文に個人情報や秘密の値がある場合は、ログを外に共有する前に取り除いてください。
const reply = await fetch('/lab/html');
const body = await reply.text();
console.log(reply.status);
console.log(reply.headers.get('content-type'));
console.log(body);
// このreplyに、さらにreply.json()を呼ばない。
本文は通常一度だけ消費できます。上でtext()を使った同じResponseに、続けてjson()を呼ぶと別の失敗を作ってしまいます。JSONの文字列をテキストとして保存した場合は、必要に応じてその文字列をJSON.parse()へ渡します。
JSONと書かれていても、構文まで保証されない
教材の/lab/invalid-jsonはContent-Typeをapplication/jsonにしながら、本文に末尾カンマを含めています。種類の表示を確かめた後でも、json()で構文の解析に失敗します。Content-Typeの確認は入口の確認であり、構文や必要な項目の保証にはなりません。
Response.json()は本文を最後まで読んでからJSONとして解析します。構文を解析できなければSyntaxError、本文がすでに使われている場合などにはTypeErrorが起こります。具体的なメッセージ全文は環境によって変わるため、教材では例外名と処理の段階を並べて表示します。MDN:Response.json()
204は本文を読まない方針を決める
204 No Contentは、成功していても応答本文がない状態です。教材では204ならjson()を呼ばず、nullを返すルールにしました。nullがfetch共通の戻り値なのではなく、この教材の設計です。実際のAPIの仕様に合わせて、空の応答を呼び出し側でどう扱うかを決めます。MDN:204 No Content
通信の失敗・読み取り中の切断・中断を分ける
HTTPの404と違い、要求が完了できなければResponseを得る前に失敗することがあります。ただし、TypeError: Failed to fetchという文字だけでは、Wi-Fi、要求先、CORSなどのどれが原因かは確定できません。ブラウザのConsoleにある補足と、Networkで対象の要求を確認します。MDN:Window.fetch()
200を受け取った後でも、本文の受信は止まる
教材の「Responseの前に接続を切る」は、HTTPヘッダーを送る前に教材サーバーが接続を切ります。これに対し「200の本文を途中で切る」は、200とヘッダー、本文の一部を送ってから切ります。
後者では、status=200を記録できた後、本文を読む処理でTypeErrorになりました。これは今回のNode.jsとChromeで確認した結果です。200が見えたことは、本文を最後まで受け取れた証拠にはなりません。この再現結果を、実サービスのすべてのTypeErrorに当てはめず、どこまで処理が進んだかを調べる材料にしてください。
AbortErrorは、自分が止めた処理かも確かめる
遅い応答を止める例も用意しました。教材の応答は約1.6秒後に返りますが、次のコードは200ミリ秒後にabort()を呼びます。中断しなければ成功することも確認しています。
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 200);
try {
const reply = await fetch('/lab/slow', {
signal: controller.signal
});
const data = await reply.json();
console.log(data);
} catch (error) {
console.log(error.name); // この教材では AbortError
} finally {
clearTimeout(timer);
}
この200ミリ秒は練習のための設定で、fetchの標準タイムアウトではありません。HTTP 408が返ったという意味でもありません。画面移動や再検索で前の要求を取り消すコードがある場合は、その中断と実際の障害を分けて扱いましょう。abort()はfetchだけでなく応答本文を読む処理も中断できます。MDN:AbortController.abort()
ローカル教材で9ケースを比べる
ZIPには教材サーバー、ブラウザ画面、2つの読み方のコード、確認用スクリプト、練習4問と解答、記入用CSVが入っています。外部APIやログインは不要で、追加パッケージも使いません。Node.jsを利用できるパソコンで動かします。
- ZIPを専用フォルダーへ展開し、そのフォルダーをターミナルで開きます。
- 下のコマンドでサーバーを起動し、表示されたURLをブラウザで開きます。
- ケースを選び、結果を予想してから「2つの読み方で実行する」を押します。
- 「HTTP確認なし」と「段階を分ける」のstatus・stage・errorNameを比べます。
- 終わったらサーバーを起動したターミナルでControl + Cを押します。
node server.mjs
# 表示された http://127.0.0.1:8772/ をブラウザで開く
HTMLのダブルクリックや通常の静的サーバーでは、失敗を作る専用ルートが動きません。8772番を使っているアプリがあれば、node server.mjs 8773のように別ポートを指定して、表示されたURLへ進んでください。
| 教材のケース | HTTP確認なし | 段階を分けた記録 |
|---|---|---|
| 200:正しいJSON | JSONを読める | ok |
| 404:JSONのエラー応答 | JSONを読める | http / 404 |
| 500:JSONのエラー応答 | JSONを読める | http / 500 |
| 200:HTML | SyntaxError | content-type(解析前に止める) |
| 200:末尾カンマのあるJSON | SyntaxError | parse |
| 204:本文なし | SyntaxError | empty(本文を読まない) |
| Responseの前に切断 | TypeError | request / statusなし |
| 200の本文を途中で切断 | TypeError | body / 200 |
| 200ms後に意図的に中断 | AbortError | aborted |
表は横にスクロールできます。stageの英単語は教材独自の記録名です。
2026年10月6日、Node.js v26.9.0とmacOSのChromeで、9ケースを2実装それぞれで実行し、表の違いを確認しました。ブラウザ画面はPC幅と390px幅でも確認しています。接続切断は教材サーバーで作るもので、CORSや実際の通信回線の障害を再現するものではありません。Windowsと実機スマートフォンの操作は未検証です。
練習:確認結果から次に調べる場所を選ぶ
次の4問では、エラー名を答えるだけでなく「次に何を確かめるか」を一文にしてください。配布ZIPの「確認メモ.csv」に、自分の観測結果を残せます。
1.404なのにJSONを読めた
「HTTP確認なし」の表示が「JSONを読み取れた」なら、データの取得は成功したと言い切れるでしょうか。
解答例:言い切れません。本文を読めてもHTTP状態は404です。まず要求先のパスと対象リソースを確かめます。reply.okを見ない実装では、この区別が抜けます。
2.200だがContent-Typeはtext/html
JSONのカンマを直す前に、何を確認するとよいでしょうか。
解答例:要求先URLと応答本文を照合します。JSONを返す場所のつもりで、別のHTML画面を受け取っていないかを確かめます。HTMLであることだけでは、ログインやURLなど原因の一つに断定できません。
3.204でjson()がSyntaxErrorになった
HTTP自体が失敗したのでしょうか。
解答例:204は成功側ですが本文がありません。APIの仕様を見て、204なら本文を読まずに扱う分岐を決めます。この教材ではnullにしています。
4.200の後、json()でTypeErrorになった
「200の本文を途中で切る」を実行した結果を、短いメモにしてください。
Response取得:できた
HTTP状態:200 / ok=true
本文形式:application/json
失敗した処理:reply.json()
例外名:TypeError
次の確認:本文の受信が最後まで完了したか
これなら「fetchが動かない」より、質問する相手に状況が伝わります。AIに修正を相談するときも、確認できた事実と未確認の推測を分けて渡しましょう。AIへコード修正を依頼する手順も参考になります。
fetchのエラー処理でよくある質問
catchを付ければ、404も処理できますか?
fetchだけでは、404のHTTP応答を理由にPromiseが失敗するとは限りません。HTTP状態を確認し、必要なら自分で例外を投げるか、状態を表す戻り値に分けます。catchに入った場合も、fetch、本文読み取り、自分が投げた例外のどこかを見ます。
Failed to fetchなら、no-corsを付ければ直りますか?
JSONを読みたい場面の一律の解決策にはなりません。no-corsで得るopaqueな応答は、JavaScriptから本文やヘッダーを読めません。まずConsoleの具体的な説明と要求先の仕様を確認し、CORSなら許可する側の設定や提供方法を調べます。この教材は同じローカル配信元へ要求するため、CORSは再現していません。
本文をログに出した後、json()で読めなくなりました
同じResponseの本文を二度読もうとしていないか確かめます。text()で読んだ文字列を保持し、必要ならその文字列を解析する方法があります。HTTP状態、本文の種類、JSONの構文、データの形を順に確かめましょう。
今回の題材で練習したのは、「エラーを消す」前に、処理が進んだ場所を言葉にすることです。画面を作る練習も合わせたい方は、JavaScriptのサンプル集から小さな操作を一つ選んでみてください。学習の進め方を相談する際は、作りたい機能と、どの段階で止まったかを整理してKredoの公式サイトで案内をご確認ください。
英語でIT・AIを学べるKredo
英語×IT・AIのスキルを身につけてグローバルに活躍しませんか?
当メディアを運営しているKredoは、英語×ITをオンラインで学ぶ「Kredoオンラインキャンプ」と、フィリピンのセブ島で英語とIT・AIを学ぶ「KredoIT留学」「KredoAI留学」を提供しています。これまでの卒業生は3,000名以上。卒業生の多くが、国内外のIT企業への転職、フリーランスなどへのキャリアチェンジを実現しています。これからの時代に必要な英語×IT・AIのスキルを身につけてグローバルに活躍しませんか?









