{"record":{"id":"6fe7a803c69864e6","repo":"golang/go","slug":"private-key-seed-not-available","errorCode":null,"errorMessage":"private key seed not available","messagePattern":"private key seed not available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/hpke/pq.go","lineNumber":320,"sourceCode":"\t}\n}\n\nfunc (kem *hybridKEM) DeriveKeyPair(ikm []byte) (PrivateKey, error) {\n\tsuiteID := byteorder.BEAppendUint16([]byte(\"KEM\"), kem.id)\n\tdk, err := SHAKE256().labeledDerive(suiteID, ikm, \"DeriveKeyPair\", nil, 32)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn kem.NewPrivateKey(dk)\n}\n\nfunc (k *hybridPrivateKey) KEM() KEM {\n\treturn k.kem\n}\n\nfunc (k *hybridPrivateKey) Bytes() ([]byte, error) {\n\tif k.seed == nil {\n\t\treturn nil, errors.New(\"private key seed not available\")\n\t}\n\treturn k.seed, nil\n}\n\nfunc (k *hybridPrivateKey) PublicKey() PublicKey {\n\treturn &hybridPublicKey{\n\t\tkem: k.kem,\n\t\tt:   k.t.PublicKey(),\n\t\tpq:  k.pq.Encapsulator(),\n\t}\n}\n\nfunc (k *hybridPrivateKey) decap(enc []byte) ([]byte, error) {\n\tif len(enc) != k.kem.pqCiphertextSize+k.kem.curvePointSize {\n\t\treturn nil, errors.New(\"invalid encapsulated key size\")\n\t}\n\tctPQ, ctT := enc[:k.kem.pqCiphertextSize], enc[k.kem.pqCiphertextSize:]\n\tssPQ, err := k.pq.Decapsulate(ctPQ)","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/hpke/pq.go#L302-L338","documentation":"Raised by hybridPrivateKey.Bytes() when k.seed is nil. The seed is only retained when the key was constructed through the generate/derive path that captures it; a key assembled from externally supplied parts has no storable seed, so returning the raw private bytes is impossible.","triggerScenarios":"Calling PrivateKey.Bytes() on a hybridPrivateKey whose seed field is nil — typically a key built via NewHybridPrivateKey from pre-existing ML-KEM and ECDH key material rather than generated/derived in-package.","commonSituations":"Serializing a hybrid private key for storage/transport after constructing it from independently generated parts; calling Bytes() on a key obtained via NewHybridPrivateKey rather than GenerateKey/DeriveKeyPair; persistence code that assumes every private key is serializable.","solutions":["Generate the key via the suite's KEM.GenerateKey() or DeriveKeyPair() so the seed is retained and Bytes() succeeds.","If you must assemble from parts, store the seed yourself at construction time instead of relying on Bytes().","Check for the error (or pre-check whether the key came from the generate path) before attempting serialization."],"exampleFix":"// before\npriv, _ := MLKEM768P256().NewPrivateKey(externalParts) // seed == nil\nseed, err := priv.Bytes() // errors\n\n// after\npriv, _ := MLKEM768P256().GenerateKey(rand.Reader) // seed retained\nseed, err := priv.Bytes() // ok","handlingStrategy":"try-catch","validationCode":"// Before serializing, confirm the key came from a seed-retaining path.\nfunc canSerialize(p hpke.PrivateKey) bool {\n    _, err := p.Bytes()\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":"if b, err := priv.Bytes(); err != nil {\n    if err.Error() == \"private key seed not available\" {\n        // regenerate from seed or refuse to persist\n    }\n    return err\n} else {\n    _ = b\n}","preventionTips":["Generate keys via KEM.GenerateKey/DeriveKeyPair when future serialization is needed.","If you assemble from parts, store the seed yourself.","Gate persistence on a successful Bytes() call."],"tags":["hpke","post-quantum","kem","serialization","hybrid"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}