OpenNHP/opennhp · error · ErrRuntimePanic
ErrRuntimePanic
ErrRuntimePanic
Error message
!!!recovered from panic: %v %s
What it means
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.
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.
Example fix
// before
mad, err = d.createMsgAssemblerData(md)
defer mad.Destroy() // panics if mad is nil
if err != nil {
return nil, err
}
// after
mad, err = d.createMsgAssemblerData(md)
if err != nil {
return nil, err
}
defer mad.Destroy() Defensive patterns
Strategy: try-catch
Validate before calling
if md == nil || len(md.Message) == 0 {
return errors.New("MsgData has no message payload")
} Type guard
func validMsgData(md *core.MsgData) bool { return md != nil && len(md.Message) > 0 } Try / catch
mad, err := dev.MsgToPacket(md)
if errors.Is(err, core.ErrRuntimePanic) {
log.Error("packet processing panic: %v", err) // includes stack via extra error
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- config load error
- unknown remote provider
- unknown remote provider
- unsupported key type, expect RSA
- JWT signing key is not initialized
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/75becf380b9d5ac6.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/device.go:327
timeout: d.LocalTransactionTimeout(),
}
d.AddLocalTransaction(t)
log.Debug("AddLocalTransaction:deviceType=%d,HeaderType=%d", d.deviceType, mad.HeaderType)
}
// send out fully encrypted packet
mad.connData.ForwardOutboundPacket(mad.BasePacket)
}()
}
}
}
// Synchronous linear processing.
func (d *Device) MsgToPacket(md *MsgData) (mad *MsgAssemblerData, err error) {
defer func() {
if x := recover(); x != nil {
mad = nil
err = fmt.Errorf("!!!recovered from panic: %v\n%s", x, string(debug.Stack()))
ErrRuntimePanic.SetExtraError(err)
err = ErrRuntimePanic
}
}()
var buf [PacketBufferSize]byte
md.ExternalPacket = &Packet{
Buf: &buf,
Content: buf[:],
HeaderType: md.HeaderType,
}
//md.Compress = len(md.Message) > 64 // no gain for compression if size is small
// use new transaction id if not specified
if md.TransactionId == 0 {
md.TransactionId = d.NextCounterIndex()
}
// process keepalive separatelyView on GitHub (pinned to 6e04ca5ff0)