{"record":{"id":"5b7314f677933eda","repo":"datawhalechina/hello-agents","slug":"error","errorCode":null,"errorMessage":"请求失败","messagePattern":"请求失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"Co-creation-projects/Apricity-InnocoreAI/frontend/static/js/app.js","lineNumber":501,"sourceCode":"\n    async request(endpoint, options = {}) {\n        const token = localStorage.getItem('token');\n        const url = `${this.baseURL}${endpoint}`;\n        \n        const config = {\n            headers: {\n                'Content-Type': 'application/json',\n                ...(token && { 'Authorization': `Bearer ${token}` }),\n                ...options.headers\n            },\n            ...options\n        };\n\n        const response = await fetch(url, config);\n        \n        if (!response.ok) {\n            const error = await response.json();\n            throw new Error(error.message || '请求失败');\n        }\n\n        return await response.json();\n    }\n\n    get(endpoint) {\n        return this.request(endpoint);\n    }\n\n    post(endpoint, data) {\n        return this.request(endpoint, {\n            method: 'POST',\n            body: JSON.stringify(data)\n        });\n    }\n\n    put(endpoint, data) {\n        return this.request(endpoint, {","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/Apricity-InnocoreAI/frontend/static/js/app.js#L483-L519","documentation":"This is a generic wrapper in a hand-rolled API client class: when fetch() returns a non-2xx response, it tries to parse the body as JSON and throws the server's message field, falling back to the literal '请求失败' (request failed). The fallback fires when the response body is not JSON or has no message field, so all server-side error detail is lost. It also does not propagate the HTTP status code onto the Error object.","triggerScenarios":"Any request through this.request() whose backend answers 4xx/5xx, or answers with non-JSON body (HTML error page, plain text, empty body from a proxy), or JSON like {detail: '...'} (FastAPI style) instead of {message: '...'}. Also response.json() itself can throw a SyntaxError on non-JSON bodies, producing a different confusing error.","commonSituations":"Backend FastAPI returns {detail}; gateway/nginx returns HTML 502; token expired returning 401 with empty body; CORS preflight failure making response.ok false; running frontend against wrong API base URL.","solutions":["Read the response body safely with text() first, try JSON.parse, and surface status code plus parsed detail/message in the thrown error","Attach status (err.status = response.status) so callers can branch on 401/403/422","Check response headers/content-type before attempting JSON parse to avoid SyntaxError replacing the real error","Verify the backend actually returns {message: ...} in its error envelope, or align the field name with the backend contract"],"exampleFix":"// before\nif (!response.ok) {\n    const error = await response.json();\n    throw new Error(error.message || '请求失败');\n}\n\n// after\nif (!response.ok) {\n    const text = await response.text().catch(() => '');\n    let message = '';\n    try { message = JSON.parse(text)?.message || JSON.parse(text)?.detail || ''; } catch (_) {}\n    const err = new Error(message || `请求失败（${response.status}）`);\n    err.status = response.status;\n    throw err;\n}","handlingStrategy":"try-catch","validationCode":"if (!token && endpointRequiresAuth) { throw new Error('未登录'); }","typeGuard":"function isApiError(e) { return e instanceof Error && typeof e.status === 'number'; }","tryCatchPattern":"try { await api.post(...) } catch (e) { if (e.status === 401) logout(); else showToast(e.message); }","preventionTips":["Always include response status on thrown errors","Parse body with text()+JSON.parse guarded by try/catch, never bare response.json()","Agree on one error envelope ({message} or {detail}) with the backend and enforce it"],"tags":["fetch","http","error-handling","json"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}