fatedier/frp · error · Error
%s
Error message
%s
What it means
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'.
Source
Thrown at pkg/util/http/error.go:31
// limitations under the License.
package http
import "fmt"
type Error struct {
Code int
Err error
}
func (e *Error) Error() string {
return e.Err.Error()
}
func NewError(code int, msg string) *Error {
return &Error{
Code: code,
Err: fmt.Errorf("%s", msg),
}
}
View on GitHub (pinned to 6c8a8d0a97)
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.
Example fix
// client of the frps REST API — before
resp, _ := http.Get(apiURL + "/proxy/tcp/not-exist")
// after
resp, err := http.Get(apiURL + "/proxy/tcp/not-exist")
if err == nil && resp.StatusCode != 200 {
body, _ := io.ReadAll(resp.Body)
log.Printf("api error %d: %s", resp.StatusCode, body)
} Defensive patterns
Strategy: try-catch
Type guard
func IsFrpHTTPError(err error) bool {
var e *http.Error // github.com/fatedier/frp/pkg/util/http
return errors.As(err, &e)
} Try / catch
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("frp api error %d: %s", resp.StatusCode, string(body))
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- HTTP ${response.status}
- HTTP ${response.status}
- envelope?.msg || `HTTP ${response.status}`
- Invalid API v2 response
- envelope.msg
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/29df4a2b3520401f.
Report an issue: GitHub.