flipped-aurora/gin-vue-admin · error
上游接口返回状态码 %d: %s
Error message
上游接口返回状态码 %d: %s
What it means
fetchPublicUpstream treats any upstream HTTP status >= 400 as an error and includes the status code plus the raw response body in the message. This is not a Go error wrap — it reflects the GVA main service rejecting the request (auth failure, validation error, 404, 500, etc.).
Source
Thrown at server/mcp/http_client.go:129
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)
}
func postUpstream[T any](ctx context.Context, endpoint string, body any) (*upstreamEnvelope[T], error) {
return doUpstream[T](ctx, http.MethodPost, endpoint, nil, body)View on GitHub (pinned to 3136500ef3)
Solutions
- Read the status code and body snippet in the message — it names the exact rejection reason
- 401/403: refresh or re-acquire the auth token and check the user's Casbin policies
- 404: compare the endpoint path against the actual GVA route registration
- 500: check GVA server logs at the corresponding endpoint for the panic/error
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: verify token validity
resp, err := getUpstream[map[string]any](ctx, "/api/user/userInfo", nil)
if err != nil && strings.Contains(err.Error(), "状态码 401") {
// token expired — re-authenticate before real calls
} Type guard
func isUpstreamStatusError(err error) (code int, body string, ok bool) {
m := regexp.MustCompile(`状态码 (\d{3}): (.*)`).FindStringSubmatch(err.Error())
if m == nil {
return 0, "", false
}
c, _ := strconv.Atoi(m[1])
return c, m[2], true
} Try / catch
data, err := getUpstream[Item](ctx, "/api/item", q)
if err != nil {
if code, body, ok := isUpstreamStatusError(err); ok {
switch {
case code == 401 || code == 403:
// refresh token / report permission problem
case code == 404:
// fix endpoint path
default:
log.Printf("upstream %d: %s", code, body)
}
}
return err
} Prevention
- Keep MCP tool endpoint paths in sync with server route registrations
- Refresh auth tokens before expiry
- Test endpoints against a running GVA instance after API changes
- Include the status code in client-facing error handling branches
When it happens
Trigger: Expired or invalid auth token sent in the upstream auth header; endpoint path wrong causing 404; request payload failing server validation causing 400; upstream panic causing 500.
Common situations: Token in context expired between MCP login and call; upstreamBaseURL pointing to a wrong port/path; API version mismatch between MCP tool endpoints and server routes; Casbin denying the MCP user's role.
Related errors
- MCP 健康检查失败: %s
- 上游大模型服务返回非 2xx: status=%d content-type=%s body=%s
- 下载技能失败, HTTP状态码: %d
- 下载压缩包失败, HTTP状态码: %d
- LLM request failed
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/f003b6dc436b799e.
Report an issue: GitHub.