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
- Inspect the wrapped error: if it is 'context deadline exceeded', increase requestTimeout() or speed up the upstream endpoint
- Verify network connectivity between the MCP process and upstreamBaseURL (curl the same URL)
- Check upstream server logs for crashes or connection resets during the request
- 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
- Use io.LimitReader to bound body size
- Set a realistic requestTimeout for slow endpoints
- Monitor upstream server stability and restarts
- Retry idempotent GET calls on transient read failures
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.