AlexxIT/go2rtc · error

multitrans: auth failed

Error message

multitrans: auth failed: ${res.Status}

What it means

In the second leg of the multitrans digest handshake, after the client sends the Authorization header, the server must reply 200 OK with a Session header. Any other status means authentication was rejected or the challenge failed, so the handshake aborts with the server's status line embedded in the message.

Solutions

  1. Verify the credentials (username/password) configured on the multitrans client match the device account.
  2. Check the device supports the digest algorithm the client uses; update firmware or client if it requires SHA-256.
  3. Re-attempt Dial in case of a stale nonce (nonce counts/nc issues); avoid sharing one device session between processes.
  4. Check device logs for auth rejection reasons (locked account, IP filtering).

Example fix

// before
client := multitrans.New(host, "admin", "wrongpass")

// after
client := multitrans.New(host, "admin", os.Getenv("CAMERA_PASSWORD"))
Defensive patterns

Strategy: retry

Validate before calling

// verify credentials with a plain digest probe before Dial
req, _ := http.NewRequest("GET", deviceURL, nil)
resp, err := httpClient.Do(req) // then perform digest round-trip manually
if err != nil || resp.StatusCode == http.StatusUnauthorized {
    return errors.New("credentials rejected by device")
}

Try / catch

var lastErr error
for i := 0; i < 3; i++ {
    if err := client.Dial(ctx); err == nil { break }
    else if strings.Contains(err.Error(), "auth failed") {
        lastErr = err
        time.Sleep(500 * time.Millisecond) // retry in case of stale nonce
        continue
    }
    return err
}

Prevention

When it happens

Trigger: Calling Dial; the digest Authorization response sent in handshake gets a non-200 status (typically 401 again after the computed digest response is wrong).

Common situations: Wrong username/password in client config; realm/nonce mismatch caused by clock skew or retries; device account locked; firmware expecting a different digest algorithm (MD5 vs SHA-256).

Related errors


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

Appendix: source

Thrown at pkg/multitrans/client.go:152

	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",
		uri, authHeader, 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.StatusOK {
		return errors.New("multitrans: auth failed: " + res.Status)
	}

	// Session: 7116520596809429228
	session := res.Header.Get("Session")
	if session == "" {
		return errors.New("multitrans: no session")
	}

	return c.openTalkChannel(uri, session)
}

func (c *Client) openTalkChannel(uri, session string) error {
	payload := `{"type":"request","seq":0,"params":{"method":"get","talk":{"mode":"full_duplex"}}}`

	data := fmt.Sprintf("MULTITRANS %s RTSP/1.0\r\nCSeq: 2\r\nSession: %s\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s",
		uri, session, len(payload), payload)

	if _, err := c.conn.Write([]byte(data)); err != nil {

View on GitHub (pinned to c245815e75)