flipped-aurora/gin-vue-admin · error

读取响应失败: %w

Error message

读取响应失败: %w

What it means

fetchPublicUpstream reads the entire upstream HTTP response body with io.ReadAll; when that read fails (connection reset mid-body, timeouts, aborted streams) it wraps the underlying error with the message '读取响应失败'. It indicates the response started but could not be fully received. The wrapped error carries the actual cause.

Source

Thrown at server/mcp/http_client.go:126

func fetchPublicUpstream[T any](path string) (*upstreamEnvelope[T], error) {
	timeoutCtx, cancel := context.WithTimeout(context.Background(), defaultUpstreamTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(timeoutCtx, http.MethodGet, upstreamURL(path), nil)
	if err != nil {
		return nil, fmt.Errorf("构建请求失败: %w", err)
	}
	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)
}

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Inspect the wrapped error: if it is 'context deadline exceeded', increase requestTimeout() or speed up the upstream endpoint
  2. Verify network connectivity between the MCP process and upstreamBaseURL (curl the same URL)
  3. Check upstream server logs for crashes or connection resets during the request
  4. Add idempotent retry logic in the MCP tool layer for GET requests

Example fix

// before
rawBody, err := io.ReadAll(resp.Body)
if err != nil {
	return nil, fmt.Errorf("读取响应失败: %w", err)
}
// after
rawBody, err := io.ReadAll(io.LimitReader(resp.Body, 10<<20))
if err != nil {
	return nil, fmt.Errorf("读取响应失败: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

func upstreamReachable(ctx context.Context, base string) error {
	c, cancel := context.WithTimeout(ctx, 3*time.Second)
	defer cancel()
	req, _ := http.NewRequestWithContext(c, http.MethodGet, base+"/health", nil)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return nil
}

Try / catch

result, err := fetchPublicUpstream[T](ctx, endpoint)
if err != nil {
	if strings.Contains(err.Error(), "读取响应失败") {
		// retry once with backoff; surface friendly message otherwise
	}
	return err
}

Prevention

When it happens

Trigger: The upstream GVA server closes the connection while the body is being read; network interruption mid-transfer; resp.Body read deadline exceeded due to the request timeout context firing during body read.

Common situations: Upstream server restarting under load, flaky internal network/DNS between MCP process and GVA main service, proxy terminating long responses, requestTimeout() too short for slow endpoints.

Related errors


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