AlexxIT/go2rtc · error

hap: wrong http status:

Error message

hap: wrong http status: 

What it means

Client.Request is the shared HTTP layer for Get/Post/Put against the HAP accessory. When the response status is >= 400 it converts it into this error, appending res.Status (e.g. "hap: wrong http status: 401 Unauthorized"). The accessory rejected the request at the HTTP level.

Solutions

  1. Read the status embedded in the error: 401/404 on session endpoints usually means re-establish the HAP session (Dial) or re-pair.
  2. For 400, validate the characteristic format/value and the query string before retrying.
  3. For 5xx, retry with backoff — accessories are embedded devices and can be temporarily busy.
  4. Verify the request path and Content-Type match what the accessory expects (PathAccessories, PathCharacteristics, PathPairings).

Example fix

// before: treating every failure the same
res, err := client.Get(hap.PathCharacteristics + "?id=" + q)
// after: branch on the HTTP status embedded in the error
res, err := client.Get(hap.PathCharacteristics + "?id=" + q)
if err != nil {
    if strings.Contains(err.Error(), "401") {
        // session lost: re-establish then retry once
        if e := client.Dial(); e == nil {
            res, err = client.Get(hap.PathCharacteristics + "?id=" + q)
        }
    }
    if err != nil { return err }
}
Defensive patterns

Strategy: try-catch

Try / catch

res, err := client.Get(path)
if err != nil {
    var status int
    if strings.HasPrefix(err.Error(), "hap: wrong http status: 4") {
        status = httpStatusFromErr(err) // 401 -> re-Dial, 404 -> bad path
    }
    switch {
    case status == 401:
        if e := client.Dial(); e == nil { res, err = client.Get(path) }
    case strings.HasPrefix(err.Error(), "hap: wrong http status: 5"):
        time.Sleep(time.Second); res, err = client.Get(path)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling Get/Post/Put with an expired or missing HAP session (401); sending a malformed characteristics write (400); requesting a path the accessory does not expose (404); hitting the accessory while it is busy (503).

Common situations: Session keys stale after the accessory rebooted; writing characteristics with wrong format/values; probing endpoints removed by a firmware update; too many concurrent connections to a constrained accessory.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/04cd610c0a071836. Report an issue: GitHub.

Appendix: source

Thrown at pkg/hap/client_http.go:44

	if c.res != nil {
		return <-c.res, c.err
	}
	return http.ReadResponse(c.reader, req)
}

func (c *Client) Request(method, path, contentType string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, "http://"+c.DeviceAddress+path, body)
	if err != nil {
		return nil, err
	}

	if contentType != "" {
		req.Header.Set("Content-Type", contentType)
	}

	res, err := c.Do(req)
	if err == nil && res.StatusCode >= http.StatusBadRequest {
		err = errors.New("hap: wrong http status: " + res.Status)
	}

	return res, err
}

func (c *Client) Get(path string) (*http.Response, error) {
	return c.Request("GET", path, "", nil)
}

func (c *Client) Post(path, contentType string, body io.Reader) (*http.Response, error) {
	return c.Request("POST", path, contentType, body)
}

func (c *Client) Put(path, contentType string, body io.Reader) (*http.Response, error) {
	return c.Request("PUT", path, contentType, body)
}

const ProtoEvent = "EVENT/1.0"

View on GitHub (pinned to c245815e75)