{"record":{"id":"6819b9182e48744a","repo":"XTLS/Xray-core","slug":"handshake-w","errorCode":null,"errorMessage":"handshake: %w","messagePattern":"handshake: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/internet/finalmask/xmc/client.go","lineNumber":243,"sourceCode":"\tc.lifecycleMu.Lock()\n\tif c.closed {\n\t\tc.lifecycleMu.Unlock()\n\t\tpacket.Stop()\n\t\treturn net.ErrClosed\n\t}\n\tc.packet = packet\n\tc.reader = packet\n\tc.writer = packet\n\tc.state = clientStateProxy\n\tc.lifecycleMu.Unlock()\n\n\treturn nil\n}\n\nfunc (c *clientConn) Read(b []byte) (int, error) {\n\terr := c.handshake()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"handshake: %w\", err)\n\t}\n\n\treturn c.reader.Read(b)\n}\n\nfunc (c *clientConn) Write(b []byte) (int, error) {\n\terr := c.handshake()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"handshake: %w\", err)\n\t}\n\n\treturn c.writer.Write(b)\n}\n\nfunc (c *clientConn) Close() error {\n\tc.lifecycleMu.Lock()\n\tc.closed = true\n\tpacket := c.packet","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/finalmask/xmc/client.go#L225-L261","documentation":"The lazy handshake (called from Read before touching c.reader) failed and the error is re-wrapped as 'handshake: %w'. This is not a distinct failure — it prefixes whichever handshake-stage error occurred (any of errors 860-875, deadline errors, or the early write/read handshake packet errors). The %w chain preserves the root cause for errors.Is/As inspection.","triggerScenarios":"The very first Read(b) on a conn returned by WrapConnClient, when the deferred xmc handshake errors for any reason: bad key, password mismatch, protocol skew, or network failure.","commonSituations":"Any first-use failure of the tunnel surfaces through Read/Write with this prefix; developers grep for 'handshake:' without realizing the actionable text is the suffix.","solutions":["Unwrap the error chain (errors.Unwrap or %v logging) and fix the underlying stage error","Run a tiny probe: call a one-byte Read immediately after connect to force the handshake early where it is easy to log","Address the root cause using the matching entry for the inner error (860-875)"],"exampleFix":"// before\nn, err := conn.Read(buf)\nif err != nil { log.Fatal(err) } // 'handshake: authentication rejected: ...'\n\n// after\nn, err := conn.Read(buf)\nif err != nil {\n    log.Printf(\"read failed: %+v\", err) // prints full wrapped chain\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() { /* handle deadline */ }\n}","handlingStrategy":"try-catch","validationCode":"// force and inspect the handshake eagerly right after connect\nprobe := make([]byte, 1)\nif _, err := cc.Read(probe); err != nil {\n    return fmt.Errorf(\"xmc handshake failed at connect time: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"n, err := conn.Read(buf)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"handshake:\") {\n        inner := errors.Unwrap(err)\n        log.Printf(\"handshake stage: %v\", inner) // actionable cause\n    }\n    return err\n}","preventionTips":["Trigger the handshake explicitly after connect instead of relying on first Read","Always log wrapped errors with %v/%+v to preserve the cause chain","Use errors.As(err, &net.Error) to separate timeouts from auth/config failures"],"tags":["handshake","error-wrapping","io","go"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}