{"record":{"id":"bae0ef99e438b171","repo":"AlexxIT/go2rtc","slug":"multitrans-expected-401-got-res-status","errorCode":null,"errorMessage":"multitrans: expected 401, got ${res.Status}","messagePattern":"multitrans: expected 401, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":401,"severity":"error","filePath":"pkg/multitrans/client.go","lineNumber":121,"sourceCode":"\nfunc (c *Client) handshake(u *url.URL) error {\n\t// Step 1: Get Challenge\n\tuid := uuid.New().String()\n\n\turi := fmt.Sprintf(\"rtsp://%s/multitrans\", u.Host)\n\tdata := fmt.Sprintf(\"MULTITRANS %s RTSP/1.0\\r\\nCSeq: 0\\r\\nX-Client-UUID: %s\\r\\n\\r\\n\", uri, uid)\n\n\tif _, err := c.conn.Write([]byte(data)); err != nil {\n\t\treturn err\n\t}\n\n\tres, err := tcp.ReadResponse(c.rd)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif res.StatusCode != http.StatusUnauthorized {\n\t\treturn errors.New(\"multitrans: expected 401, got \" + res.Status)\n\t}\n\n\tauth := res.Header.Get(\"WWW-Authenticate\")\n\trealm := tcp.Between(auth, `realm=\"`, `\"`)\n\tnonce := tcp.Between(auth, `nonce=\"`, `\"`)\n\n\t// Step 2: Send Auth\n\tuser := u.User.Username()\n\tpass, _ := u.User.Password()\n\n\tha1 := tcp.HexMD5(user, realm, pass)\n\tha2 := tcp.HexMD5(\"MULTITRANS\", uri)\n\tresponse := tcp.HexMD5(ha1, nonce, ha2)\n\n\tauthHeader := fmt.Sprintf(`Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"`,\n\t\tuser, realm, nonce, uri, response)\n\n\tdata = fmt.Sprintf(\"MULTITRANS %s RTSP/1.0\\r\\nCSeq: 1\\r\\nAuthorization: %s\\r\\nX-Client-UUID: %s\\r\\n\\r\\n\",","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/multitrans/client.go#L103-L139","documentation":"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.","triggerScenarios":"Calling multitrans client.Dial; during handshake, the first tcp.ReadResponse returns a status other than 401 (e.g. 200, 403, 404).","commonSituations":"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.","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)."],"exampleFix":"// before\nc, err := multitrans.Dial(ctx, \"http://192.168.1.50:80\")\n\n// after\n// ensure correct port/path for the multitrans endpoint\nc, err := multitrans.Dial(ctx, \"http://192.168.1.50:8595\")","handlingStrategy":"validation","validationCode":"// pre-flight: confirm the endpoint issues a digest challenge\nresp, err := http.Get(deviceURL)\nif err != nil || resp.StatusCode != http.StatusUnauthorized ||\n    !strings.Contains(resp.Header.Get(\"WWW-Authenticate\"), \"realm=\") {\n    return errors.New(\"endpoint does not provide multitrans digest challenge\")\n}","typeGuard":null,"tryCatchPattern":"if err := client.Dial(ctx); err != nil {\n    var e *string\n    if errors.As(err, &e) && strings.Contains(err.Error(), \"expected 401\") {\n        // wrong device/port or firmware without challenge; surface config advice\n        return fmt.Errorf(\"device did not start digest handshake: %w\", err)\n    }\n    return err\n}","preventionTips":["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."],"tags":["http","authentication","handshake","device"],"backgroundTag":"unexpected-http-status","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}