{"record":{"id":"7c21497e8a4b1a74","repo":"fatedier/frp","slug":"decode-clienthello-transcript-w","errorCode":null,"errorMessage":"decode ClientHello transcript: %w","messagePattern":"decode ClientHello transcript: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proto/wire/crypto.go","lineNumber":135,"sourceCode":"\nfunc selectUDPPacketCodec(codecs []string) string {\n\tif Supports(codecs, UDPPacketCodecBinary) {\n\t\treturn UDPPacketCodecBinary\n\t}\n\treturn \"\"\n}\n\nfunc NewCryptoContext(algorithm string, clientHelloPayload, serverHelloPayload []byte) *CryptoContext {\n\treturn &CryptoContext{\n\t\tAlgorithm:      algorithm,\n\t\tTranscriptHash: HashCryptoTranscript(clientHelloPayload, serverHelloPayload),\n\t}\n}\n\nfunc NewClientCryptoContext(clientHelloPayload, serverHelloPayload []byte) (*CryptoContext, error) {\n\tvar clientHello ClientHello\n\tif err := json.Unmarshal(clientHelloPayload, &clientHello); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode ClientHello transcript: %w\", err)\n\t}\n\tvar serverHello ServerHello\n\tif err := json.Unmarshal(serverHelloPayload, &serverHello); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode ServerHello transcript: %w\", err)\n\t}\n\tif err := ValidateServerHelloForClient(clientHello, serverHello); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewCryptoContext(serverHello.Selected.Crypto.Algorithm, clientHelloPayload, serverHelloPayload), nil\n}\n\nfunc HashCryptoTranscript(clientHelloPayload, serverHelloPayload []byte) []byte {\n\th := sha256.New()\n\t_, _ = h.Write([]byte(cryptoTranscriptLabel))\n\twriteCryptoTranscriptPart(h, \"client hello\", clientHelloPayload)\n\twriteCryptoTranscriptPart(h, \"server hello\", serverHelloPayload)\n\treturn h.Sum(nil)","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/proto/wire/crypto.go#L117-L153","documentation":"Thrown by NewClientCryptoContext when the stored ClientHello transcript payload fails to json.Unmarshal. The function re-decodes both handshake payloads to build the client's CryptoContext, so the client hello bytes must still be valid JSON of the ClientHello shape.","triggerScenarios":"Passing a clientHelloPayload that was modified, truncated, or captured from a different protocol version; passing the raw frame header plus JSON instead of payload only; passing nil or an empty slice.","commonSituations":"Test code that captures the wrong byte slice (e.g. the whole frame instead of Frame.Payload); a relay or proxy that re-encodes the payload; logging code that strips characters from the payload before replay.","solutions":["Pass exactly the JSON payload bytes that were sent on the wire — Frame.Payload after ReadFrame, not the framed bytes.","Validate the payload with json.Valid() before calling NewClientCryptoContext to get a clearer failure point.","If replaying a captured session, capture payloads at the frame layer, before any transformation."],"exampleFix":"// before\nctx, err := wire.NewClientCryptoContext(frameBytes, serverBytes) // frameBytes includes 8-byte header\n\n// after\nf, _ := conn.ReadFrame()\nctx, err := wire.NewClientCryptoContext(f.Payload, serverBytes)","handlingStrategy":"validation","validationCode":"if !json.Valid(clientHelloPayload) {\n    return errors.New(\"client hello transcript is not valid JSON — capture Frame.Payload only\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := wire.NewClientCryptoContext(clientRaw, serverRaw); err != nil {\n    if strings.HasPrefix(err.Error(), \"decode ClientHello\") {\n        // wrong bytes captured: re-capture payload at frame layer, do not retry blindly\n    }\n}","preventionTips":["Store the exact wire bytes (Frame.Payload) for transcript use — never re-marshal decoded structs.","The transcript hash must cover original bytes; any re-encoding breaks it even if JSON parses."],"tags":["crypto","json","handshake","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}