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

  1. Read the response body — it carries the specific msg, and the HTTP status equals e.Code.
  2. Fix the underlying request per the message (correct proxy name, valid parameters, fresh token).
  3. 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

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


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/29df4a2b3520401f. Report an issue: GitHub.