AlexxIT/go2rtc · error
multitrans: expected 401, got
Error message
multitrans: expected 401, got ${res.Status} What it means
The multitrans client's handshake expects the camera/device to respond to the initial request with HTTP 401 Unauthorized containing a WWW-Authenticate digest challenge. Any other status code means the device did not start a digest-auth challenge sequence, so the handshake aborts. This is a protocol-expectation check, not a plain HTTP failure.
Solutions
- Verify the target host/port actually runs the multitrans (digest-challenge) service; test the URL in a browser and confirm a 401 with WWW-Authenticate header.
- Check the device settings to ensure HTTP/anycast talk service is enabled and authentication is required.
- Remove or bypass proxies/firewalls that could rewrite or block the 401 challenge response.
- Confirm firmware version matches the protocol this client implements (some firmwares skip the 401 challenge).
Example fix
// before c, err := multitrans.Dial(ctx, "http://192.168.1.50:80") // after // ensure correct port/path for the multitrans endpoint c, err := multitrans.Dial(ctx, "http://192.168.1.50:8595")
Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: confirm the endpoint issues a digest challenge
resp, err := http.Get(deviceURL)
if err != nil || resp.StatusCode != http.StatusUnauthorized ||
!strings.Contains(resp.Header.Get("WWW-Authenticate"), "realm=") {
return errors.New("endpoint does not provide multitrans digest challenge")
} Try / catch
if err := client.Dial(ctx); err != nil {
var e *string
if errors.As(err, &e) && strings.Contains(err.Error(), "expected 401") {
// wrong device/port or firmware without challenge; surface config advice
return fmt.Errorf("device did not start digest handshake: %w", err)
}
return err
} Prevention
- Confirm the device URL/port before integration (browser should show a 401 auth prompt).
- Keep device firmware aligned with the protocol version the client supports.
- Avoid proxies in front of cameras that alter HTTP challenge responses.
- Document the exact port/path for the talk service per device model.
When it happens
Trigger: Calling multitrans client.Dial; during handshake, the first tcp.ReadResponse returns a status other than 401 (e.g. 200, 403, 404).
Common situations: Pointing the client at a non-multitrans device or wrong port so a different HTTP server answers; device firmware that does not require auth; a proxy returning 403/502; device web UI disabled.
Related errors
- multitrans: auth failed
- milesone: authentication failed:
- wrong response:
- wrong auth response
- multitrans: no session
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/bae0ef99e438b171.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/multitrans/client.go:121
func (c *Client) handshake(u *url.URL) error {
// Step 1: Get Challenge
uid := uuid.New().String()
uri := fmt.Sprintf("rtsp://%s/multitrans", u.Host)
data := fmt.Sprintf("MULTITRANS %s RTSP/1.0\r\nCSeq: 0\r\nX-Client-UUID: %s\r\n\r\n", uri, uid)
if _, err := c.conn.Write([]byte(data)); err != nil {
return err
}
res, err := tcp.ReadResponse(c.rd)
if err != nil {
return err
}
if res.StatusCode != http.StatusUnauthorized {
return errors.New("multitrans: expected 401, got " + res.Status)
}
auth := res.Header.Get("WWW-Authenticate")
realm := tcp.Between(auth, `realm="`, `"`)
nonce := tcp.Between(auth, `nonce="`, `"`)
// Step 2: Send Auth
user := u.User.Username()
pass, _ := u.User.Password()
ha1 := tcp.HexMD5(user, realm, pass)
ha2 := tcp.HexMD5("MULTITRANS", uri)
response := tcp.HexMD5(ha1, nonce, ha2)
authHeader := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"`,
user, realm, nonce, uri, response)
data = fmt.Sprintf("MULTITRANS %s RTSP/1.0\r\nCSeq: 1\r\nAuthorization: %s\r\nX-Client-UUID: %s\r\n\r\n",View on GitHub (pinned to c245815e75)