{"record":{"id":"69ee78f091ac2c97","repo":"flipped-aurora/gin-vue-admin","slug":"error-69ee78","errorCode":null,"errorMessage":"上游接口路径不能为空","messagePattern":"上游接口路径不能为空","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mcp/http_client.go","lineNumber":162,"sourceCode":"}\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)\n}\n\nfunc deleteUpstream[T any](ctx context.Context, endpoint string, body any) (*upstreamEnvelope[T], error) {\n\treturn doUpstream[T](ctx, http.MethodDelete, endpoint, nil, body)\n}\n\nfunc doUpstream[T any](ctx context.Context, method, endpoint string, query url.Values, body any) (*upstreamEnvelope[T], error) {\n\ttoken := authTokenFromContext(ctx)\n\tif token == \"\" {\n\t\treturn nil, fmt.Errorf(\"缺少MCP鉴权请求头: %s\", configuredAuthHeader())\n\t}\n\n\tendpoint = strings.TrimSpace(endpoint)\n\tif endpoint == \"\" {\n\t\treturn nil, fmt.Errorf(\"上游接口路径不能为空\")\n\t}\n\tif !strings.HasPrefix(endpoint, \"/\") {\n\t\tendpoint = \"/\" + endpoint\n\t}\n\n\tbaseURL := upstreamBaseURL()\n\trequestURL, err := url.Parse(baseURL + endpoint)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"构建上游请求地址失败: %w\", err)\n\t}\n\tif len(query) > 0 {\n\t\trequestURL.RawQuery = query.Encode()\n\t}\n\n\tvar reader io.Reader\n\tif body != nil {\n\t\tpayload, err := json.Marshal(body)\n\t\tif err != nil {","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/mcp/http_client.go#L144-L180","documentation":"doUpstream validates that the endpoint argument is non-empty after trimming whitespace; an empty path means no upstream API was specified, so the request is rejected early. This is a programming/configuration guard, not a runtime network condition.","triggerScenarios":"A constant or config value holding the endpoint path is empty; a function building the path from parts returned \"\"; a tool handler passes an unset variable to getUpstream/postUpstream/deleteUpstream.","commonSituations":"Missing env var or config key for a configurable endpoint; refactoring left a placeholder empty; path assembled with strings that ended up blank.","solutions":["Check the call site of getUpstream/postUpstream/deleteUpstream for the empty endpoint value","Define endpoints as non-empty constants in one place instead of inline strings","Add a startup assertion/config validation that all endpoint paths are set","Log the calling tool name to identify which handler passed the empty path"],"exampleFix":"// before\nconst userDetailEndpoint = \"\"\nresp, err := getUpstream[User](ctx, userDetailEndpoint, nil)\n// after\nconst userDetailEndpoint = \"/api/user/detail\"\nresp, err := getUpstream[User](ctx, userDetailEndpoint, nil)","handlingStrategy":"validation","validationCode":"func safeEndpoint(path string) (string, error) {\n\tpath = strings.TrimSpace(path)\n\tif path == \"\" {\n\t\treturn \"\", errors.New(\"endpoint path must not be empty\")\n\t}\n\treturn path, nil\n}","typeGuard":null,"tryCatchPattern":"result, err := getUpstream[Data](ctx, endpoint, nil)\nif err != nil {\n\tif strings.Contains(err.Error(), \"上游接口路径不能为空\") {\n\t\t// log the tool name + call site; it is a config/code bug\n\t}\n\treturn err\n}","preventionTips":["Define endpoint paths as package-level constants, never computed empty values","Validate config-driven endpoint values at startup","Add unit tests for every tool handler asserting its endpoint constant is non-empty","Avoid building paths dynamically from optional inputs without a default"],"tags":["configuration","validation","go"],"backgroundTag":"empty-endpoint-path","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}