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

  1. Point the client at frpc's admin address (webServer.addr:webServer.port from frpc.toml), not at the frps server or a proxy port.
  2. Curl http://<admin-addr>/api/status manually and inspect the body shown in the error to identify what is actually answering.
  3. 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

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.