{"record":{"id":"e2043489ce25e715","repo":"fatedier/frp","slug":"decode-serverhello-transcript-w","errorCode":null,"errorMessage":"decode ServerHello transcript: %w","messagePattern":"decode ServerHello transcript: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proto/wire/crypto.go","lineNumber":139,"sourceCode":"\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)\n}\n\nfunc writeCryptoTranscriptPart(h hash.Hash, label string, payload []byte) {\n\tvar length [8]byte","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/proto/wire/crypto.go#L121-L157","documentation":"Thrown by NewClientCryptoContext when the stored ServerHello transcript payload fails to json.Unmarshal. Like the client-hello check, it guards that the transcript bytes used for the transcript hash are the exact JSON the server sent.","triggerScenarios":"Passing a serverHelloPayload that is not valid ServerHello JSON: truncated frame payload, wrong frame's payload, a plain-text error page read instead of the hello, or an empty slice.","commonSituations":"Reading the server hello with the wrong frame type expectation and feeding whatever came first; a server that closed mid-handshake leaving a partial payload; tests hand-crafting the server payload as a map that marshals differently than expected.","solutions":["Read the ServerHello with ReadJSONFrame(frameTypeServerHello, &hello) and keep the exact f.Payload bytes for NewCryptoContext.","Log the offending payload (truncated) when this error fires — the wrapped json error tells you the byte offset of the syntax problem.","Ensure the server actually completed its hello before the client captures the bytes."],"exampleFix":"// before\nvar hello wire.ServerHello\n_ = conn.ReadJSONFrame(wire.FrameTypeServerHello, &hello)\nraw, _ := json.Marshal(hello) // re-marshaled, not the wire bytes\nctx, err := wire.NewClientCryptoContext(clientRaw, raw)\n\n// after\nf, err := conn.ReadFrame()\nif err != nil { return err }\nctx, err := wire.NewClientCryptoContext(clientRaw, f.Payload)","handlingStrategy":"validation","validationCode":"if !json.Valid(serverHelloPayload) {\n    return errors.New(\"server hello transcript is not valid JSON\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := wire.NewClientCryptoContext(clientRaw, serverRaw); err != nil {\n    if strings.HasPrefix(err.Error(), \"decode ServerHello\") {\n        // log truncated payload; likely wrong frame captured or server died mid-hello\n    }\n}","preventionTips":["Keep the raw server hello payload from ReadFrame alongside the decoded struct.","Treat a failed server hello decode as a dead connection — never reuse the stream."],"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"}