{"record":{"id":"f003b6dc436b799e","repo":"flipped-aurora/gin-vue-admin","slug":"d-s","errorCode":null,"errorMessage":"上游接口返回状态码 %d: %s","messagePattern":"上游接口返回状态码 (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mcp/http_client.go","lineNumber":129,"sourceCode":"\n\treq, err := http.NewRequestWithContext(timeoutCtx, http.MethodGet, upstreamURL(path), nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"构建请求失败: %w\", err)\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"请求上游接口失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\trawBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\tif resp.StatusCode >= http.StatusBadRequest {\n\t\treturn nil, fmt.Errorf(\"上游接口返回状态码 %d: %s\", resp.StatusCode, string(rawBody))\n\t}\n\n\tvar result upstreamEnvelope[T]\n\tif err := json.Unmarshal(rawBody, &result); err != nil {\n\t\treturn nil, fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\tif result.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"上游接口业务错误: %s\", result.Msg)\n\t}\n\treturn &result, nil\n}\n\nfunc getUpstream[T any](ctx context.Context, endpoint string, query url.Values) (*upstreamEnvelope[T], error) {\n\treturn doUpstream[T](ctx, http.MethodGet, endpoint, query, nil)\n}\n\nfunc postUpstream[T any](ctx context.Context, endpoint string, body any) (*upstreamEnvelope[T], error) {\n\treturn doUpstream[T](ctx, http.MethodPost, endpoint, nil, body)","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/mcp/http_client.go#L111-L147","documentation":"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.).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-flight: verify token validity\nresp, err := getUpstream[map[string]any](ctx, \"/api/user/userInfo\", nil)\nif err != nil && strings.Contains(err.Error(), \"状态码 401\") {\n\t// token expired — re-authenticate before real calls\n}","typeGuard":"func isUpstreamStatusError(err error) (code int, body string, ok bool) {\n\tm := regexp.MustCompile(`状态码 (\\d{3}): (.*)`).FindStringSubmatch(err.Error())\n\tif m == nil {\n\t\treturn 0, \"\", false\n\t}\n\tc, _ := strconv.Atoi(m[1])\n\treturn c, m[2], true\n}","tryCatchPattern":"data, err := getUpstream[Item](ctx, \"/api/item\", q)\nif err != nil {\n\tif code, body, ok := isUpstreamStatusError(err); ok {\n\t\tswitch {\n\t\tcase code == 401 || code == 403:\n\t\t\t// refresh token / report permission problem\n\t\tcase code == 404:\n\t\t\t// fix endpoint path\n\t\tdefault:\n\t\t\tlog.Printf(\"upstream %d: %s\", code, body)\n\t\t}\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["http","http-status","api"],"backgroundTag":"upstream-http-error-status","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}