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
- Read the status embedded in the error: 401/404 on session endpoints usually means re-establish the HAP session (Dial) or re-pair.
- For 400, validate the characteristic format/value and the query string before retrying.
- For 5xx, retry with backoff — accessories are embedded devices and can be temporarily busy.
- 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
- Re-establish the HAP session (Dial) whenever you start seeing 4xx on previously working calls.
- Validate characteristic IDs, formats, and query strings before writes to avoid 400s.
- Back off on 5xx — HAP accessories are constrained embedded devices.
- Log res.Status values to spot firmware updates that changed endpoint availability.
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
- gopro: wrong response:
- hap: no free streams
- hap: GetAccessories zero answer
- hap: PairVerify with unknown client_id:
- hap: unsupported path:
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)