{"record":{"id":"75becf380b9d5ac6","repo":"OpenNHP/opennhp","slug":"errruntimepanic","errorCode":"ErrRuntimePanic","errorMessage":"!!!recovered from panic: %v\n%s","messagePattern":"!!!recovered from panic: (.+?)\n(.+?)","errorType":"error_code","errorClass":"ErrRuntimePanic","httpStatus":null,"severity":"error","filePath":"nhp/core/device.go","lineNumber":327,"sourceCode":"\t\t\t\t\t\ttimeout:       d.LocalTransactionTimeout(),\n\t\t\t\t\t}\n\t\t\t\t\td.AddLocalTransaction(t)\n\t\t\t\t\tlog.Debug(\"AddLocalTransaction:deviceType=%d,HeaderType=%d\", d.deviceType, mad.HeaderType)\n\t\t\t\t}\n\n\t\t\t\t// send out fully encrypted packet\n\t\t\t\tmad.connData.ForwardOutboundPacket(mad.BasePacket)\n\t\t\t}()\n\t\t}\n\t}\n}\n\n// Synchronous linear processing.\nfunc (d *Device) MsgToPacket(md *MsgData) (mad *MsgAssemblerData, err error) {\n\tdefer func() {\n\t\tif x := recover(); x != nil {\n\t\t\tmad = nil\n\t\t\terr = fmt.Errorf(\"!!!recovered from panic: %v\\n%s\", x, string(debug.Stack()))\n\t\t\tErrRuntimePanic.SetExtraError(err)\n\t\t\terr = ErrRuntimePanic\n\t\t}\n\t}()\n\n\tvar buf [PacketBufferSize]byte\n\tmd.ExternalPacket = &Packet{\n\t\tBuf:        &buf,\n\t\tContent:    buf[:],\n\t\tHeaderType: md.HeaderType,\n\t}\n\t//md.Compress = len(md.Message) > 64 // no gain for compression if size is small\n\t// use new transaction id if not specified\n\tif md.TransactionId == 0 {\n\t\tmd.TransactionId = d.NextCounterIndex()\n\t}\n\n\t// process keepalive separately","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/device.go#L309-L345","documentation":"Device.MsgToPacket wraps any panic raised while converting a message into an NHP packet (assembler setup, crypto, packet construction) into ErrRuntimePanic. The formatted message embeds the panic value plus a debug.Stack() trace via SetExtraError, so a malformed or unexpected message cannot crash the daemon's processing goroutine.","triggerScenarios":"Any panic inside MsgToPacket's processing path — nil pointer in message assembly (e.g. mad.Destroy deferred on nil mad), index/nil deref on malformed MsgData, or panics from underlying crypto routines on unexpected input.","commonSituations":"Feeding crafted/corrupt network messages into the device loop; passing MsgData with missing fields (empty Message, zero TransactionId handling edge cases); version skew between endpoints producing unexpected header types.","solutions":["Log and inspect the wrapped stack trace (ErrRuntimePanic extra error) to find the panicking line.","Validate MsgData fields (HeaderType, Message non-empty, sizes) before calling MsgToPacket.","Reproduce with the offending packet capture and fix the nil/slice bug at the panicking frame.","Check peer/library version alignment if the panic correlates with a specific sender's messages.","Watch for the mad.Destroy() deferred call on a nil mad when createMsgAssemblerData fails — a known nil-deref pattern in this function."],"exampleFix":"// before\nmad, err = d.createMsgAssemblerData(md)\ndefer mad.Destroy() // panics if mad is nil\nif err != nil {\n    return nil, err\n}\n// after\nmad, err = d.createMsgAssemblerData(md)\nif err != nil {\n    return nil, err\n}\ndefer mad.Destroy()","handlingStrategy":"try-catch","validationCode":"if md == nil || len(md.Message) == 0 {\n    return errors.New(\"MsgData has no message payload\")\n}","typeGuard":"func validMsgData(md *core.MsgData) bool { return md != nil && len(md.Message) > 0 }","tryCatchPattern":"mad, err := dev.MsgToPacket(md)\nif errors.Is(err, core.ErrRuntimePanic) {\n    log.Error(\"packet processing panic: %v\", err) // includes stack via extra error\n    return err\n}","preventionTips":["Validate MsgData completeness before feeding the device loop.","Read the stack trace stored in ErrRuntimePanic's extra error to locate the bug.","Fuzz-test MsgToPacket with malformed messages to surface nil-deref paths (e.g. nil mad with deferred Destroy).","Keep endpoint library versions aligned to avoid unexpected header/field shapes."],"tags":["go","panic","runtime","packet-processing"],"backgroundTag":"internal-invariant-violation","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"}