fatedier/frp · error
unmarshal http response error: %s
Error message
unmarshal http response error: %s
What it means
Returned by Client.GetProxyStatus when the frpc admin API's /api/status response body is not valid JSON for model.StatusResp. The raw body is included in the message instead of the unmarshal error, which usually reveals an HTML error page or auth failure text coming back instead of JSON.
Source
Thrown at pkg/sdk/client/client.go:46
}
func (c *Client) SetAuth(user, pwd string) {
c.authUser = user
c.authPwd = pwd
}
func (c *Client) GetProxyStatus(ctx context.Context, name string) (*model.ProxyStatusResp, error) {
req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/status", nil)
if err != nil {
return nil, err
}
content, err := c.do(req)
if err != nil {
return nil, err
}
allStatus := make(model.StatusResp)
if err = json.Unmarshal([]byte(content), &allStatus); err != nil {
return nil, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(content))
}
for _, pss := range allStatus {
for _, ps := range pss {
if ps.Name == name {
return &ps, nil
}
}
}
return nil, fmt.Errorf("no proxy status found")
}
func (c *Client) GetAllProxyStatus(ctx context.Context) (model.StatusResp, error) {
req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/status", nil)
if err != nil {
return nil, err
}
content, err := c.do(req)
if err != nil {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Point the client at frpc's admin address (webServer.addr:webServer.port from frpc.toml), not at the frps server or a proxy port.
- Curl http://<admin-addr>/api/status manually and inspect the body shown in the error to identify what is actually answering.
- Remove any reverse-proxy layer that rewrites or wraps the admin API responses.
Example fix
// before
client := client.New("127.0.0.1:7000") // frps bind port, wrong
// after
// use frpc webServer port, e.g. webServer.port = 7400 in frpc.toml
client := client.New("127.0.0.1:7400") Defensive patterns
Strategy: validation
Validate before calling
// verify the admin API answers JSON before relying on it
resp, err := http.Get("http://" + c.address + "/api/status")
if err != nil { return err }
if ct := resp.Header.Get("Content-Type"); !strings.Contains(ct, "application/json") {
return fmt.Errorf("%s is not the frpc admin API (content-type %s)", c.address, ct)
} Try / catch
if _, err := client.GetProxyStatus(ctx, name); err != nil {
if strings.Contains(err.Error(), "unmarshal http response") {
// wrong endpoint answering: check webServer.port in frpc.toml, do not retry
}
} Prevention
- Construct the SDK client from the same webServer.host/port values as frpc.toml.
- Smoke-test GET /api/status with curl when setting up the admin server.
When it happens
Trigger: Calling GetProxyStatus against an address that is not the frpc admin server (e.g. the proxy port, or a reverse proxy returning 200 with an HTML page); the admin API returning a success status but a non-JSON body; a body truncated mid-transfer.
Common situations: Wrong webServer.port/address in frpc config; a proxy or load balancer in front of frpc rewriting responses; an auth token that yields a 200-wrapped error page from some middleware.
Related errors
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/bbd4118e4a906623.
Report an issue: GitHub.