---
title: "Sửa lỗi Failed to fetch và phân biệt 3 nguyên nhân"
description: "TypeError: Failed to fetch nghĩa là request không nhận được phản hồi HTTP. Thiếu header CORS, máy chủ không chạy và host không tồn tại đều cho cùng một thông điệp này."
canonical: "https://logbrew.co/vi/errors/failed-to-fetch"
last_updated: "2026-11-10"
---

# Sửa lỗi Failed to fetch và phân biệt 3 nguyên nhân

`TypeError: Failed to fetch` nghĩa là request không nhận được phản hồi HTTP. Thiếu header CORS, máy chủ không chạy và host không tồn tại đều cho cùng một thông điệp này.

Đã thử nghiệm ngày 10 tháng 11, 2026 với Chromium 152, @logbrew/browser 0.1.21 và @logbrew/sdk 0.1.44

Tác giả [Furkan Erday](https://github.com/furkanerday). Furkan Erday là nhà sáng lập LogBrew. Anh xây dựng LogBrew bằng Claude Code và Codex mỗi ngày.

Canonical: https://logbrew.co/vi/errors/failed-to-fetch

## Điều gì chỉ ra nguyên nhân của một `fetch()` thất bại

| Điều gì đã sai | `error.message` | Console |
| --- | --- | --- |
| Không có header `Access-Control-Allow-Origin` | `Failed to fetch` | Chính sách CORS |
| Không có gì lắng nghe trên cổng | `Failed to fetch` | `ERR_CONNECTION_REFUSED` |
| Host không có bản ghi DNS | `Failed to fetch` | `ERR_NAME_NOT_RESOLVED` |
| Máy chủ trả về 500 | Không reject | `500` |

4 request tới một API ở origin khác. Cốc đầy nghĩa là đoạn văn bản nêu được nguyên nhân.

## Tìm xem bạn gặp nguyên nhân nào

### Mở console của trình duyệt

`fetch()` reject với cùng một `TypeError: Failed to fetch` cho mọi lỗi mạng. Dòng console bên cạnh mới nêu nguyên nhân thật.

### API trả lời mà không có header CORS

Console báo `blocked by CORS policy`. Máy chủ có trả lời, nhưng origin của trang không được phép đọc phản hồi, nên trình duyệt bỏ phản hồi đó đi.

Kết quả

```text
Access to fetch at 'http://127.0.0.1:4874/api/closed' from origin 'http://127.0.0.1:4873' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```

### Không có gì lắng nghe trên host và cổng đó

Console báo `net::ERR_CONNECTION_REFUSED`. API chưa chạy, hoặc cổng trong URL sai. Khởi động API, rồi kiểm tra cổng.

Kết quả

```text
Failed to load resource: net::ERR_CONNECTION_REFUSED
```

### Tên host không phân giải được

Console báo `net::ERR_NAME_NOT_RESOLVED`. DNS không có bản ghi cho host này. Kiểm tra hostname có bị gõ sai không, và biến môi trường chứa URL của API trong lần triển khai này.

Kết quả

```text
Failed to load resource: net::ERR_NAME_NOT_RESOLVED
```

## Sửa trong mã của bạn

### Gửi header CORS từ API

Trả về `Access-Control-Allow-Origin` đặt bằng origin của trang bạn, hoặc `*` cho API công khai. Trong lần thử của chúng tôi, cùng một endpoint đi từ `Failed to fetch` sang `HTTP 200` chỉ nhờ thêm header đó.

```bash
Access-Control-Allow-Origin: https://app.example.com
```

### Kiểm tra status, vì 500 không reject

`fetch()` vẫn trả về kết quả với mọi phản hồi HTTP, kể cả 404 và 500. Trong lần thử của chúng tôi, một 500 với body `{"error":"tap jammed"}` đi vào nhánh `then`, không phải `catch`. Kiểm tra `response.ok` trước khi đọc body.

```bash
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status} for ${url}`);
```

### Ghi log URL cùng với lỗi

Một `fetch()` bị reject không có phản hồi, và stack trace của nó trỏ vào mã của bạn, không phải nguyên nhân. Bắt lỗi ở nơi bạn gọi `fetch()` và giữ method cùng URL kèm theo lỗi, để hệ thống theo dõi lỗi của bạn cho biết request nào thất bại.

```bash
try {
  return await fetch(url);
} catch (error) {
  console.error("fetch failed", url, error);
  throw error;
}
```

## Kết quả khi chúng tôi thử nghiệm

Trang chạy tại `http://127.0.0.1:4873` trên Chromium 152 với `@logbrew/browser` 0.1.21 và phần instrumentation cho fetch đã cài. Một API tại `http://127.0.0.1:4874` trả lời 3 đường dẫn, và chúng tôi gửi 5 request không có `catch`, nên mỗi lần reject đều tới SDK dưới dạng rejection chưa được xử lý.

`/api/closed` trả về 200 không có header CORS, và request bị reject với `TypeError: Failed to fetch`. Một request tới cổng 4875, nơi không có gì lắng nghe, và một request tới `http://keg.invalid/api` bị reject với cùng lỗi. Trong cả 3 trường hợp, `error.message` là `Failed to fetch`, `error.name` là `TypeError`, và stack trace trỏ vào chính lời gọi `fetch()` của chúng tôi.

Console phân biệt được chúng. Trường hợp đầu báo `blocked by CORS policy`, trường hợp thứ hai `net::ERR_CONNECTION_REFUSED`, trường hợp thứ ba `net::ERR_NAME_NOT_RESOLVED`. `/api/broken`, trả về 500 kèm header, không hề reject, còn `/api/open`, trả về 200 kèm header, cho `HTTP 200 {"ok":true}`.

## Vì sao mọi nguyên nhân đều có cùng thông điệp

Chuẩn Fetch gộp mọi thất bại trước khi có phản hồi thành 1 thứ, lỗi mạng, tức là một phản hồi có type `error` và status 0. Thất bại CORS cũng vậy. Ở chế độ `cors`, tài nguyên không hiểu giao thức CORS sẽ cho lỗi mạng.

Phương thức `fetch()` reject promise của nó bằng `TypeError` khi gặp lỗi mạng, và chỉ khi đó. MDN cũng nói như vậy. Promise chỉ bị reject khi request thất bại, ví dụ URL sai định dạng hoặc lỗi mạng, chứ không phải với status HTTP như 404 hay 504.

Trang MDN về lỗi CORS nói rằng chi tiết của một request CORS thất bại không được cung cấp cho JavaScript vì lý do bảo mật, và console là nơi duy nhất để tìm chúng. whatwg/fetch#526 đã đề nghị các loại lỗi rõ ràng hơn từ năm 2017 và vẫn đang mở.

## LogBrew ghi nhận những gì

Mỗi lần reject đến dưới dạng 1 vấn đề tên `Unhandled promise rejection: TypeError`, có stack frame trong bundle của chúng tôi và khóa gom nhóm `browser.unhandledrejection:TypeError:/app.js`. Nó cho biết bạn gọi `fetch()` ở đâu, chứ không phải điều gì thất bại.

Phần instrumentation cho fetch thêm một span trong cùng trace, `browser.fetch GET /api/closed`, với status `error`, method, đường dẫn request và `errorType` `TypeError`. Span nêu tên request thất bại dù lỗi thì không.

Phản hồi 500 không tạo vấn đề nào, chỉ có một span với status `error` và `statusCode` 500. Phản hồi 200 tạo một span với status `ok`. Vậy nên trong LogBrew, một 500 và một lỗi mạng là 2 span khác nhau, trong khi ở mã của bạn chỉ trường hợp thứ hai đi vào `catch`.

## Câu hỏi thường gặp

### TypeError Failed to fetch nghĩa là gì?

Trình duyệt không lấy được phản hồi HTTP cho một lời gọi `fetch()`. Máy chủ không thể truy cập, host không phân giải được, hoặc CORS đã chặn phản hồi. Thông điệp giống nhau trong cả 3 trường hợp.

### Failed to fetch có luôn là lỗi CORS không?

Không. Trong lần thử của chúng tôi, thiếu header CORS, cổng không có máy chủ và host không có bản ghi DNS đều cho thông điệp này. Chỉ dòng console bên cạnh mới nêu nguyên nhân.

### Vì sao fetch không reject với 404 hay 500?

Chuẩn Fetch chỉ reject khi có lỗi mạng. Phản hồi HTTP với bất kỳ status nào vẫn là phản hồi, nên `fetch()` trả về kết quả và bạn tự kiểm tra `response.ok`.

### Làm sao bắt lỗi Failed to fetch trong JavaScript?

Bọc lời gọi trong `try` và `catch`, hoặc thêm `.catch()` vào promise. Lỗi là một `TypeError` không có phản hồi, nên bạn cần tự ghi log URL và method.

### Failed to fetch có nghĩa là máy chủ sập không?

Đôi khi. Máy chủ không chạy cho `net::ERR_CONNECTION_REFUSED` trong console. Cùng `Failed to fetch` đó cũng đến từ thiếu header CORS hoặc tên host không có bản ghi DNS.

## Xem request nào thất bại, và vì sao

SDK trình duyệt của LogBrew ghi lại mỗi `fetch()` dưới dạng span với method, đường dẫn và status, trong cùng trace với lỗi mà nó gây ra. Bắt đầu với gói miễn phí rộng rãi, không cần thẻ.

[Bắt đầu miễn phí](https://logbrew.co/vi)

## Nguồn

- [MDN, Window: fetch() method](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch)
- [MDN, CORS errors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors)
- [Fetch Standard, The fetch() method](https://fetch.spec.whatwg.org/#fetch-method)
- [whatwg/fetch#526](https://github.com/whatwg/fetch/issues/526)

## Thêm cách sửa lỗi JavaScript

- [Sửa lỗi Script error khiến thông điệp, file và số dòng bị ẩn](https://logbrew.co/vi/errors/script-error)
- [Những lỗi JavaScript chúng tôi đã tái hiện, và cách sửa từng lỗi](https://logbrew.co/vi/errors)
