---
title: "修复 Failed to fetch 错误，分清它的 3 种原因"
description: "TypeError: Failed to fetch 表示请求没有得到 HTTP 响应。缺少 CORS 响应头、服务器没有运行、主机不存在，给出的都是同一条消息。"
canonical: "https://logbrew.co/zh-Hans/errors/failed-to-fetch"
last_updated: "2026-11-10"
---

# 修复 Failed to fetch 错误，分清它的 3 种原因

`TypeError: Failed to fetch` 表示请求没有得到 HTTP 响应。缺少 CORS 响应头、服务器没有运行、主机不存在，给出的都是同一条消息。

2026年11月10日 用 Chromium 152、@logbrew/browser 0.1.21和@logbrew/sdk 0.1.44 实测

作者 [Furkan Erday](https://github.com/furkanerday). Furkan Erday 是 LogBrew 的创始人，每天用 Claude Code 和 Codex 开发 LogBrew。

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

## 什么能说明 `fetch()` 失败的原因

| 出了什么问题 | `error.message` | 控制台 |
| --- | --- | --- |
| 没有 `Access-Control-Allow-Origin` 响应头 | `Failed to fetch` | CORS 策略 |
| 端口上没有服务在监听 | `Failed to fetch` | `ERR_CONNECTION_REFUSED` |
| 主机没有 DNS 记录 | `Failed to fetch` | `ERR_NAME_NOT_RESOLVED` |
| 服务器返回了 500 | 未 reject | `500` |

向另一个源上的 API 发出 4 个请求。满杯表示文本说明了原因。

## 找出你遇到的是哪种原因

### 打开浏览器控制台

`fetch()` 对每一种网络错误都用同一个 `TypeError: Failed to fetch` 来 reject。旁边那行控制台输出才说明真正的原因。

### API 回应了，但没有 CORS 响应头

控制台显示 `blocked by CORS policy`。服务器回应了，但页面的源没有被允许读取响应，所以浏览器丢弃了它。

输出

```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.
```

### 那个主机和端口上没有服务在监听

控制台显示 `net::ERR_CONNECTION_REFUSED`。API 没有运行，或者 URL 里的端口不对。先启动 API，再检查端口。

输出

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

### 主机名无法解析

控制台显示 `net::ERR_NAME_NOT_RESOLVED`。DNS 没有这个主机的记录。检查主机名有没有拼错，再检查本次部署中存放 API URL 的环境变量。

输出

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

## 在你的代码里修复

### 让 API 发送 CORS 响应头

返回 `Access-Control-Allow-Origin`，值设为你页面的源，公开 API 则设为 `*`。在我们的实测中，同一个接口只加了这个响应头，就从 `Failed to fetch` 变成了 `HTTP 200`。

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

### 检查状态码，因为 500 不会 reject

`fetch()` 对任何 HTTP 响应都会正常返回，包括 404 和 500。在我们的实测中，一个正文为 `{"error":"tap jammed"}` 的 500 进入了 `then` 分支，而不是 `catch`。读取正文前先检查 `response.ok`。

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

### 把 URL 和错误一起记录

被 reject 的 `fetch()` 没有响应，它的堆栈指向你的代码，而不是原因。在你调用 `fetch()` 的地方捕获错误，把方法和 URL 连同错误一起保留，这样你的错误监控才能说明是哪个请求失败了。

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

## 我们实测看到的结果

页面运行在 `http://127.0.0.1:4873`，使用 Chromium 152，安装了 `@logbrew/browser` 0.1.21 和 fetch 插桩。位于 `http://127.0.0.1:4874` 的一个 API 响应 3 个路径，我们发出了 5 个没有 `catch` 的请求，所以每次 reject 都以未处理的 rejection 形式到达 SDK。

`/api/closed` 返回 200 但没有 CORS 响应头，请求以 `TypeError: Failed to fetch` 被 reject。一个发往端口 4875 的请求，那里没有服务在监听，和一个发往 `http://keg.invalid/api` 的请求，都以同样的错误被 reject。在这 3 种情况下，`error.message` 都是 `Failed to fetch`，`error.name` 都是 `TypeError`，堆栈都指向我们自己的 `fetch()` 调用。

控制台能把它们区分开。第一种显示 `blocked by CORS policy`，第二种 `net::ERR_CONNECTION_REFUSED`，第三种 `net::ERR_NAME_NOT_RESOLVED`。带响应头返回 500 的 `/api/broken` 完全没有 reject，带响应头返回 200 的 `/api/open` 给出了 `HTTP 200 {"ok":true}`。

## 为什么每种原因的消息都一样

Fetch 标准把响应到达之前的所有失败都折叠成 1 种东西，网络错误，也就是 type 为 `error`、status 为 0 的响应。CORS 失败也包括在内。在 `cors` 模式下，不理解 CORS 协议的资源会产生网络错误。

`fetch()` 方法遇到网络错误时用 `TypeError` 来 reject 它的 promise，而且只在这时。MDN 也是这么说的。promise 只在请求失败时被 reject，比如 URL 格式错误或网络错误，而不会因为 404 或 504 这样的 HTTP 状态码。

MDN 关于 CORS 错误的页面说，出于安全原因，失败的 CORS 请求的详情不会提供给 JavaScript，控制台是唯一能找到它们的地方。whatwg/fetch#526 从 2017 年起就在提议更明确的错误类型，至今仍然开着。

## LogBrew 记录了什么

每次 reject 都以 1 个名为 `Unhandled promise rejection: TypeError` 的问题到达，带有指向我们 bundle 的堆栈帧和聚合键 `browser.unhandledrejection:TypeError:/app.js`。它告诉你在哪里调用了 `fetch()`，而不是什么失败了。

fetch 插桩在同一条链路里加上一个 Span，`browser.fetch GET /api/closed`，状态为 `error`，带有方法、请求路径，以及值为 `TypeError` 的 `errorType`。错误没有说出失败的是哪个请求，Span 说出来了。

500 响应不产生任何问题，只有一个状态为 `error`、`statusCode` 为 500 的 Span。200 响应产生一个状态为 `ok` 的 Span。所以在 LogBrew 里，一个 500 和一个网络错误是 2 个不同的 Span，而在你的代码里，只有后者会进入 `catch`。

## 常见问题

### TypeError Failed to fetch 是什么意思？

浏览器没能为一次 `fetch()` 调用拿到 HTTP 响应。服务器无法连接，主机无法解析，或者 CORS 阻止了响应。这 3 种情况下的消息完全一样。

### Failed to fetch 一定是 CORS 错误吗？

不一定。在我们的实测中，缺少 CORS 响应头、端口上没有服务器、主机没有 DNS 记录，都给出了这条消息。只有旁边那行控制台输出才说明原因。

### fetch 为什么不会在 404 或 500 时 reject？

Fetch 标准只在网络错误时 reject。任何状态码的 HTTP 响应都仍然是响应，所以 `fetch()` 正常返回，由你自己检查 `response.ok`。

### 在 JavaScript 里怎么捕获 Failed to fetch？

把调用包在 `try` 和 `catch` 里，或者给 promise 加上 `.catch()`。这个错误是一个没有响应的 `TypeError`，所以要自己记录 URL 和方法。

### Failed to fetch 是否表示服务器挂了？

有时是。没有运行的服务器会在控制台给出 `net::ERR_CONNECTION_REFUSED`。同样的 `Failed to fetch` 也会来自缺少的 CORS 响应头，或者没有 DNS 记录的主机名。

## 看清哪个请求失败了，以及为什么

LogBrew 浏览器 SDK 把每次 `fetch()` 记录为一个 Span，带有方法、路径和状态，并和它引发的错误放在同一条链路中。免费额度充足，无需绑卡。

[免费开始](https://logbrew.co/zh-Hans)

## 参考来源

- [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)

## 更多 JavaScript 错误修复

- [修复隐藏了消息、文件和行号的脚本错误](https://logbrew.co/zh-Hans/errors/script-error)
- [我们复现过的 JavaScript 错误，以及每个错误的修复方法](https://logbrew.co/zh-Hans/errors)
