datawhalechina/hello-agents · error

请求失败

Error message

请求失败

What it means

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.

Source

Thrown at Co-creation-projects/Apricity-InnocoreAI/frontend/static/js/app.js:501

    async request(endpoint, options = {}) {
        const token = localStorage.getItem('token');
        const url = `${this.baseURL}${endpoint}`;
        
        const config = {
            headers: {
                'Content-Type': 'application/json',
                ...(token && { 'Authorization': `Bearer ${token}` }),
                ...options.headers
            },
            ...options
        };

        const response = await fetch(url, config);
        
        if (!response.ok) {
            const error = await response.json();
            throw new Error(error.message || '请求失败');
        }

        return await response.json();
    }

    get(endpoint) {
        return this.request(endpoint);
    }

    post(endpoint, data) {
        return this.request(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }

    put(endpoint, data) {
        return this.request(endpoint, {

View on GitHub (pinned to 606a07d341)

Solutions

  1. Read the response body safely with text() first, try JSON.parse, and surface status code plus parsed detail/message in the thrown error
  2. Attach status (err.status = response.status) so callers can branch on 401/403/422
  3. Check response headers/content-type before attempting JSON parse to avoid SyntaxError replacing the real error
  4. Verify the backend actually returns {message: ...} in its error envelope, or align the field name with the backend contract

Example fix

// before
if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message || '请求失败');
}

// after
if (!response.ok) {
    const text = await response.text().catch(() => '');
    let message = '';
    try { message = JSON.parse(text)?.message || JSON.parse(text)?.detail || ''; } catch (_) {}
    const err = new Error(message || `请求失败(${response.status})`);
    err.status = response.status;
    throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!token && endpointRequiresAuth) { throw new Error('未登录'); }

Type guard

function isApiError(e) { return e instanceof Error && typeof e.status === 'number'; }

Try / catch

try { await api.post(...) } catch (e) { if (e.status === 401) logout(); else showToast(e.message); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/5b7314f677933eda. Report an issue: GitHub.