MeWrite Docs

AbortError: The operation was aborted

Fetch APIでリクエストがキャンセルされた場合に発生するエラー

概要

AbortError: The operation was aborted は、Fetch APIのリクエストが AbortController によってキャンセルされた場合に発生するエラーです。タイムアウト処理やコンポーネントのアンマウント時のクリーンアップで意図的に発生させることが多いですが、予期しない中断も原因となります。

エラーメッセージ

DOMException: The operation was aborted.
AbortError: The user aborted a request.
Uncaught (in promise) DOMException: The operation was aborted.

原因

1. 意図的なキャンセル(正常動作)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const controller = new AbortController();

fetch('/api/data', { signal: controller.signal })
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Request was cancelled');
    }
  });

// リクエストをキャンセル
controller.abort();

2. Reactコンポーネントのアンマウント

コンポーネントがアンマウントされた後にstateを更新しようとする場合です。

1
2
3
4
5
6
// 問題: クリーンアップなし
useEffect(() => {
  fetch('/api/data')
    .then(res => res.json())
    .then(data => setData(data)); // アンマウント後に実行される可能性
}, []);

3. タイムアウト

リクエストが時間内に完了しなかった場合です。

1
2
3
4
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

fetch('/api/data', { signal: controller.signal });

4. ブラウザによる自動キャンセル

ページ遷移時にブラウザが実行中のリクエストをキャンセルします。

解決策

1. エラーハンドリングの追加

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
async function fetchData() {
  try {
    const response = await fetch('/api/data', { signal: controller.signal });
    return await response.json();
  } catch (err) {
    if (err.name === 'AbortError') {
      // キャンセルは正常動作として扱う
      console.log('Fetch was cancelled');
      return null;
    }
    // その他のエラーは再throw
    throw err;
  }
}

2. Reactでのクリーンアップ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
useEffect(() => {
  const controller = new AbortController();

  fetch('/api/data', { signal: controller.signal })
    .then(res => res.json())
    .then(data => setData(data))
    .catch(err => {
      if (err.name !== 'AbortError') {
        setError(err);
      }
    });

  // クリーンアップ関数
  return () => controller.abort();
}, []);

3. カスタムフックの作成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();

    setLoading(true);
    fetch(url, { signal: controller.signal })
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      })
      .catch(err => {
        if (err.name !== 'AbortError') {
          setError(err);
          setLoading(false);
        }
      });

    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

4. タイムアウト付きfetch

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
async function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    return response;
  } catch (err) {
    if (err.name === 'AbortError') {
      throw new Error(`Request timed out after ${timeout}ms`);
    }
    throw err;
  } finally {
    clearTimeout(timeoutId);
  }
}

5. AbortSignal.timeout()(モダンブラウザ)

1
2
3
4
// Node.js 17.3+ / モダンブラウザ
fetch('/api/data', {
  signal: AbortSignal.timeout(5000)
});

よくある間違い

AbortControllerの再利用

1
2
3
4
5
6
7
8
// NG: 一度abortしたcontrollerは再利用不可
const controller = new AbortController();
controller.abort();
fetch('/api/data', { signal: controller.signal }); // 即座にAbortError

// OK: 新しいcontrollerを作成
const newController = new AbortController();
fetch('/api/data', { signal: newController.signal });

関連エラー

関連エラー

JavaScript の他のエラー

最終更新: 2025-12-17