flipped-aurora/gin-vue-admin · error

result.Msg

Error message

result.Msg

What it means

In doUpstream, when the upstream HTTP response status is >= 400 and the decoded response body carries a non-empty result.Msg, that message is surfaced verbatim as the error via errors.New(result.Msg). The literal string 'result.Msg' in an error log means the upstream returned an error status but the Msg field was empty or the message content itself is this placeholder — i.e. the real upstream failure text was lost.

Source

Thrown at server/mcp/http_client.go:226

	if err != nil {
		return nil, fmt.Errorf("读取上游响应失败: %w", err)
	}

	var result upstreamEnvelope[T]
	if len(rawBody) > 0 {
		if err := json.Unmarshal(rawBody, &result); err != nil {
			// 上游返回非 JSON(如 404/502 的 HTML 或网关错误页):先暴露真实状态码,
			// 不让解析错误掩盖真实的 HTTP 失败
			if resp.StatusCode >= http.StatusBadRequest {
				return nil, fmt.Errorf("上游请求失败,状态码: %d,响应: %s", resp.StatusCode, truncateUpstreamBody(rawBody))
			}
			return nil, fmt.Errorf("解析上游响应失败: %w", err)
		}
	}

	if resp.StatusCode >= http.StatusBadRequest {
		if result.Msg != "" {
			return nil, errors.New(result.Msg)
		}
		return nil, fmt.Errorf("上游请求失败,状态码: %d", resp.StatusCode)
	}

	if result.Code != 0 {
		if result.Msg != "" {
			return nil, errors.New(result.Msg)
		}
		return nil, fmt.Errorf("上游请求失败,业务码: %d", result.Code)
	}

	return &result, nil
}

// doUpstreamRaw 是动态 tool 专用的上游调用:接受完整 path(已替换路径参数)、method、query、body,
// 返回原始响应字节(动态 tool 把响应原样包成 MCP text content 返回给外部 AI)。
// 认证头从 ctx 取(与 doUpstream 一致,由外部 AI 通过 MCP 请求头透传 token)。
func doUpstreamRaw(ctx context.Context, method, path string, query url.Values, body any) (int, []byte, error) {

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Log the full upstream status code and raw body alongside this error to see the real failure reason.
  2. Check the upstream service is running and the URL/path in the MCP config is correct.
  3. Verify the upstream response follows the {code, data, msg} convention; if not, fix the server or the decoder.
  4. Improve the fallback: include StatusCode in the error even when Msg is empty (e.g. fmt.Errorf("%s (HTTP %d)", result.Msg, resp.StatusCode)).

Example fix

// before
return nil, errors.New(result.Msg)
// after
if result.Msg != "" {
    return nil, fmt.Errorf("%s (HTTP %d)", result.Msg, resp.StatusCode)
}
return nil, fmt.Errorf("上游请求失败,状态码: %d", resp.StatusCode)
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: verify upstream reachable before batch work
resp, err := http.Get(upstreamHealthURL)
if err != nil || resp.StatusCode >= 400 {
    return errors.New("upstream service unavailable; aborting MCP call")
}

Try / catch

resp, err := client.postUpstream(url, payload)
if err != nil {
    var statusErr interface{ StatusCode() int }
    // log status + raw body for diagnosis since Msg may be empty
    log.Printf("upstream failed: %v", err)
    return fmt.Errorf("upstream call failed: %w", err)
}

Prevention

When it happens

Trigger: Any getUpstream/postUpstream/deleteUpstream call where the upstream replies with status >= 400 and result.Msg is empty, causing the placeholder to be recorded; or an upstream explicitly sending Msg == "result.Msg".

Common situations: Upstream service returning a bare error status (404/500) with an empty or non-standard body; response shape mismatch so JSON decoding fills nothing into Msg; gateway/proxy intercepting the request and returning a non-{code,msg} body.

Related errors


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