{"record":{"id":"29df4a2b3520401f","repo":"fatedier/frp","slug":"s-29df4a","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pkg/util/http/error.go","lineNumber":31,"sourceCode":"// limitations under the License.\n\npackage http\n\nimport \"fmt\"\n\ntype Error struct {\n\tCode int\n\tErr  error\n}\n\nfunc (e *Error) Error() string {\n\treturn e.Err.Error()\n}\n\nfunc NewError(code int, msg string) *Error {\n\treturn &Error{\n\t\tCode: code,\n\t\tErr:  fmt.Errorf(\"%s\", msg),\n\t}\n}\n","sourceCodeStart":13,"sourceCodeEnd":34,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/util/http/error.go#L13-L34","documentation":"This is the Error() method of pkg/util/http.Error, the wrapper frp uses to attach an HTTP status Code to an error message (NewError(code, msg) formats msg with \"%s\"). When such an error bubbles to an HTTP handler, the transport layer maps e.Code to the response status and the message becomes the body. The \"%s\" string itself is just the format; the observable text is whatever msg was passed, e.g. 'format url error' or 'proxy not found'.","triggerScenarios":"REST endpoints under /api (frps/frpc dashboards) returning non-2xx: requesting a nonexistent proxy's status, invalid API parameters, or auth failures. The concrete message comes from each call site of NewError/LibError.","commonSituations":"Dashboard/API clients querying a proxy name that does not exist; expired or missing API bearer token paths; version-skewed dashboards calling endpoints whose parameters changed.","solutions":["Read the response body — it carries the specific msg, and the HTTP status equals e.Code.","Fix the underlying request per the message (correct proxy name, valid parameters, fresh token).","When calling the API programmatically, check resp.StatusCode and surface the body text rather than only the status."],"exampleFix":"// client of the frps REST API — before\nresp, _ := http.Get(apiURL + \"/proxy/tcp/not-exist\")\n\n// after\nresp, err := http.Get(apiURL + \"/proxy/tcp/not-exist\")\nif err == nil && resp.StatusCode != 200 {\n    body, _ := io.ReadAll(resp.Body)\n    log.Printf(\"api error %d: %s\", resp.StatusCode, body)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func IsFrpHTTPError(err error) bool {\n    var e *http.Error // github.com/fatedier/frp/pkg/util/http\n    return errors.As(err, &e)\n}","tryCatchPattern":"resp, err := client.Do(req)\nif err != nil {\n    return err\n}\nif resp.StatusCode >= 400 {\n    body, _ := io.ReadAll(resp.Body)\n    return fmt.Errorf(\"frp api error %d: %s\", resp.StatusCode, string(body))\n}","preventionTips":["Always surface response body text, not just the status code — it contains the specific msg.","Use errors.As(*http.Error) to extract e.Code when the error crosses in-process boundaries.","Keep dashboard and binary versions aligned so endpoint parameters match."],"tags":["http","api","error-wrapping","dashboard"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}