flipped-aurora/gin-vue-admin · error

解析响应失败: %w

Error message

解析响应失败: %w

What it means

The upstream returned HTTP 200 but its JSON body could not be unmarshaled into upstreamEnvelope[T]; the error is wrapped as '解析响应失败'. This means the response is not the expected {code,msg,data} envelope shape (or data does not match T).

Source

Thrown at server/mcp/http_client.go:134

	req.Header.Set("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("请求上游接口失败: %w", err)
	}
	defer resp.Body.Close()

	rawBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %w", err)
	}
	if resp.StatusCode >= http.StatusBadRequest {
		return nil, fmt.Errorf("上游接口返回状态码 %d: %s", resp.StatusCode, string(rawBody))
	}

	var result upstreamEnvelope[T]
	if err := json.Unmarshal(rawBody, &result); err != nil {
		return nil, fmt.Errorf("解析响应失败: %w", err)
	}
	if result.Code != 0 {
		return nil, fmt.Errorf("上游接口业务错误: %s", result.Msg)
	}
	return &result, nil
}

func getUpstream[T any](ctx context.Context, endpoint string, query url.Values) (*upstreamEnvelope[T], error) {
	return doUpstream[T](ctx, http.MethodGet, endpoint, query, nil)
}

func postUpstream[T any](ctx context.Context, endpoint string, body any) (*upstreamEnvelope[T], error) {
	return doUpstream[T](ctx, http.MethodPost, endpoint, nil, body)
}

func deleteUpstream[T any](ctx context.Context, endpoint string, body any) (*upstreamEnvelope[T], error) {
	return doUpstream[T](ctx, http.MethodDelete, endpoint, nil, body)
}

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Log/inspect string(rawBody) to see what the upstream actually returned
  2. Verify the generic type parameter T matches the endpoint's response data model
  3. Check whether a proxy or auth middleware is returning HTML instead of JSON
  4. Align T with the current server response struct after any API change

Example fix

// before
type upstreamEnvelope[T any] struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data T      `json:"data"`
}
// after (tolerate data being absent)
var result upstreamEnvelope[T]
if len(bytes.TrimSpace(rawBody)) == 0 {
	return nil, fmt.Errorf("解析响应失败: 空响应体")
}
if err := json.Unmarshal(rawBody, &result); err != nil {
	return nil, fmt.Errorf("解析响应失败: %w", err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// sniff the response shape before trusting it
if !strings.HasPrefix(strings.TrimSpace(string(rawBody)), "{") {
	// not JSON — likely an HTML proxy/login page; fix base URL or auth

Type guard

func isValidEnvelope[T any](raw []byte) (*upstreamEnvelope[T], bool) {
	var probe struct {
		Code *int `json:"code"`
	}
	if err := json.Unmarshal(raw, &probe); err != nil || probe.Code == nil {
		return nil, false
	}
	var env upstreamEnvelope[T]
	if err := json.Unmarshal(raw, &env); err != nil {
		return nil, false
	}
	return &env, true
}

Try / catch

data, err := fetchPublicUpstream[Item](ctx, endpoint)
if err != nil {
	if strings.Contains(err.Error(), "解析响应失败") {
		// log raw body upstream of here; treat as upstream contract violation
	}
	return err
}

Prevention

When it happens

Trigger: Upstream returns HTML (login page/proxy error page) with 200; upstream response shape changed; generic type T does not match the actual data field type (e.g. expecting array but getting object); empty body with 200.

Common situations: Reverse proxy intercepting requests and serving an HTML error/login page; a GVA endpoint updated its response model; wrong generic type parameter passed to fetchPublicUpstream; gzip/charset issues served by a misconfigured proxy.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/03b4f8b5346f6a1f. Report an issue: GitHub.