From 95af3059668f2425008086a2306188243ab9ccd7 Mon Sep 17 00:00:00 2001 From: CPTProgrammer <46586216+CPTProgrammer@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:29:54 +0800 Subject: [PATCH] Include error response body in API client errors --- src/lib/curseforge/client.ts | 11 ++++++++--- src/lib/modrinth/client.ts | 8 ++++++-- src/lib/utils/fetch.ts | 12 ++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 src/lib/utils/fetch.ts diff --git a/src/lib/curseforge/client.ts b/src/lib/curseforge/client.ts index 0f13fe0..7a55539 100644 --- a/src/lib/curseforge/client.ts +++ b/src/lib/curseforge/client.ts @@ -1,3 +1,5 @@ +import { readErrorBody } from "../utils/fetch"; + /** CurseForge API 客户端,封装两套 API 的鉴权与 User-Agent。 */ export class CurseForgeClient { /** GET /v1/mods/{modId}/files 等只读接口 */ @@ -38,8 +40,9 @@ export class CurseForgeClient { headers: this.apiHeaders(), }); if (!res.ok) { + const body = await readErrorBody(res); throw new Error( - `CurseForge API GET ${path} failed: ${res.status} ${res.statusText}`, + `CurseForge API GET ${path} failed: ${res.status} ${res.statusText}\n${body}`, ); } return res.json() as Promise; @@ -51,8 +54,9 @@ export class CurseForgeClient { headers: this.uploadHeaders(), }); if (!res.ok) { + const body = await readErrorBody(res); throw new Error( - `CurseForge Upload GET ${path} failed: ${res.status} ${res.statusText}`, + `CurseForge Upload GET ${path} failed: ${res.status} ${res.statusText}\n${body}`, ); } return res.json() as Promise; @@ -69,8 +73,9 @@ export class CurseForgeClient { body, }); if (!res.ok) { + const resBody = await readErrorBody(res); throw new Error( - `CurseForge Upload POST ${path} failed: ${res.status} ${res.statusText}`, + `CurseForge Upload POST ${path} failed: ${res.status} ${res.statusText}\n${resBody}`, ); } return res.json() as Promise; diff --git a/src/lib/modrinth/client.ts b/src/lib/modrinth/client.ts index 086bc8a..89cd472 100644 --- a/src/lib/modrinth/client.ts +++ b/src/lib/modrinth/client.ts @@ -1,3 +1,5 @@ +import { readErrorBody } from "../utils/fetch"; + /** Modrinth API v2 的基础请求客户端,封装鉴权与 User-Agent。 */ export class ModrinthClient { private base = "https://api.modrinth.com/v2"; @@ -26,8 +28,9 @@ export class ModrinthClient { headers: this.headers(), }); if (!res.ok) { + const body = await readErrorBody(res); throw new Error( - `Modrinth API GET ${path} failed: ${res.status} ${res.statusText}`, + `Modrinth API GET ${path} failed: ${res.status} ${res.statusText}\n${body}`, ); } return res.json() as Promise; @@ -44,8 +47,9 @@ export class ModrinthClient { body, }); if (!res.ok) { + const resBody = await readErrorBody(res); throw new Error( - `Modrinth API POST ${path} failed: ${res.status} ${res.statusText}`, + `Modrinth API POST ${path} failed: ${res.status} ${res.statusText}\n${resBody}`, ); } return res.json() as Promise; diff --git a/src/lib/utils/fetch.ts b/src/lib/utils/fetch.ts new file mode 100644 index 0000000..885bce9 --- /dev/null +++ b/src/lib/utils/fetch.ts @@ -0,0 +1,12 @@ +/** + * 读取错误响应体文本(最多 4096 字符),用于拼入错误信息。 + * 若读取失败则返回兜底字符串。 + */ +export async function readErrorBody(res: Response): Promise { + try { + const text = await res.text(); + return text.slice(0, 4096); + } catch { + return "(unable to read response body)"; + } +}