{"record":{"id":"9df98f8e2b3f5f80","repo":"OpenNHP/opennhp","slug":"missing-remote-peer-public-key","errorCode":null,"errorMessage":"missing remote peer public key","messagePattern":"missing remote peer public key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/initiator.go","lineNumber":50,"sourceCode":"\tClPkc          bool // 0: non-CL-PKC extented, 1: CL-PKC extended\n\tExternalPacket *Packet\n\tExternalCookie *[CookieSize]byte\n\tMessage        []byte\n\tPeerPk         []byte\n\tEncryptedPktCh chan *MsgAssemblerData\n\tResponseMsgCh  chan *PacketParserData\n}\n\nfunc (d *Device) validateMsgData(md *MsgData) (err error) {\n\tif md.PrevParserData == nil {\n\t\tif d.deviceType == NHP_SERVER && md.ConnData == nil {\n\t\t\terr = fmt.Errorf(\"missing connection data for server\")\n\t\t} else if d.deviceType != NHP_SERVER && md.RemoteAddr == nil {\n\t\t\terr = fmt.Errorf(\"missing remote address\")\n\t\t}\n\n\t\tif md.PeerPk == nil {\n\t\t\terr = fmt.Errorf(\"missing remote peer public key\")\n\t\t}\n\t}\n\n\treturn err\n}\n\ntype MsgAssemblerData struct {\n\tdevice     *Device\n\tBasePacket *Packet\n\tconnData   *ConnectionData\n\tciphers    *CipherSuite\n\n\tdeviceEcdh     Ecdh\n\tephermeralEcdh Ecdh\n\theader         Header\n\thmacHash       hash.Hash\n\tchainHash      hash.Hash\n\tbodyAead       cipher.AEAD","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/initiator.go#L32-L68","documentation":"Device.validateMsgData always requires md.PeerPk (the remote peer's static public key) when PrevParserData is nil, regardless of device type. The peer key is needed to verify and encrypt messages for the counterparty; sending without it is impossible.","triggerScenarios":"Any MsgToPacket or server-side message assembly where MsgData.PeerPk is nil and PrevParserData is nil — even when ConnData or RemoteAddr is correctly set.","commonSituations":"Loading peer keys from a stale or empty server.toml peer table; key rotation (e.g. after generate-nhp-keys.sh --regenerate) leaving code with the wrong key id; tests building MsgData without a peer key.","solutions":["Look up the peer's public key from the device's peer table and set md.PeerPk before sending","Redeploy synchronized peer tables after key rotation (see AGENTS.md key rotation notes)","In tests, generate peer keys via keygen helpers instead of leaving PeerPk nil","Confirm the config file lists the remote peer so the device can resolve its public key"],"exampleFix":"// before\nmd := &MsgData{Msg: msg, RemoteAddr: addr}\n// after\npeer, ok := dev.GetPeer(remotePubKeyBase64)\nif !ok { return errors.New(\"unknown peer\") }\nmd := &MsgData{Msg: msg, RemoteAddr: addr, PeerPk: peer.PublicKey}","handlingStrategy":"validation","validationCode":"if md.PeerPk == nil {\n    return errors.New(\"PeerPk required\")\n}","typeGuard":"func hasPeerKey(md *nhpcore.MsgData) bool { return md.PeerPk != nil }","tryCatchPattern":"if err := dev.MsgToPacket(md); err != nil && strings.Contains(err.Error(), \"missing remote peer public key\") {\n    return fmt.Errorf(\"peer %s not in peer table\", peerId)\n}","preventionTips":["Resolve peer keys from the device peer table before every send","After key rotation, redeploy all peer tables in lockstep","Add startup checks that all configured peers have public keys"],"tags":["go","cryptography","keys"],"backgroundTag":"missing-credentials","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}