OpenNHP/opennhp · error · ErrDataDecompressionFailed
ErrDataDecompressionFailed
ErrDataDecompressionFailed
Error message
decompressed size %d exceeds limit %d
What it means
In decryptBody, after successfully decompressing the packet body, the responder checks the decompressed output size n against maxDecompressedSize. If the output exceeds the limit, it logs a critical message, attaches the size detail to ErrDataDecompressionFailed, and returns that sentinel error. This defends against decompression bombs — a small malicious compressed body that would expand to exhaust responder memory.
Solutions
- Reduce the message size on the sending side — chunk or split the payload into multiple NHP messages so each decompressed body fits the limit.
- If the traffic is trusted and legitimately large, raise maxDecompressedSize in the responder configuration and ensure adequate memory headroom.
- Verify both peers run compatible versions with matching compression/size-limit settings.
- If the packet came from an unknown source, treat it as an attack: the error already returns ErrDataDecompressionFailed; check logs (log.Critical) for the source address and block it.
Example fix
// before: sender packs an entire huge payload into one message
sendNhpMessage(compress(entireDataset)) // decompressed > maxDecompressedSize
// after: chunk the payload
for chunk := range split(entireDataset, maxChunkSize) {
sendNhpMessage(compress(chunk))
} Defensive patterns
Strategy: try-catch
Validate before calling
// client-side: check uncompressed size before compressing/sending
const maxDecompressedSize = 1 << 20 // match responder limit
if len(body) > maxDecompressedSize {
return fmt.Errorf("payload %d exceeds limit %d; chunk it before sending", len(body), maxDecompressedSize)
}
compressed := compress(body) Try / catch
if errors.Is(err, nhpcore.ErrDataDecompressionFailed) {
// decompressed body exceeded maxDecompressedSize (or failed to decompress)
log.Printf("oversized/invalid compressed body from %s: %v", srcAddr, err)
// drop connection / block source; do not retry same payload
} Prevention
- Chunk large payloads client-side so each message decompresses under the limit
- Keep maxDecompressedSize configuration consistent across peer versions
- Cap message size at the application layer before compression
- Treat repeated decompression-limit hits from one address as a decompression-bomb attack and block the source
When it happens
Trigger: A received NHP packet body that is flagged as compressed decompresses to more than maxDecompressedSize bytes: either an attacker sending a crafted decompression bomb, or a legitimate peer compressing an oversized message (huge transaction payload, oversized plugin/auth data) that exceeds the server's configured limit.
Common situations: DoS attempts using gzip/flate bombs against the responder, clients transmitting very large auth or transaction messages without chunking, mismatched maxDecompressedSize configuration between peers after a version upgrade that raised the limit on one side only.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- invalid resource ID: potential path traversal attack
- invalid file name
- unexpected signing method
- attestation verification failed
- peer does not match its previous address on this connection
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/6c559c5f582f72c4.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/responder.go:690
if err != nil {
log.Critical("invalid compressed data: %v", err)
ErrDataDecompressionFailed.SetExtraError(err)
return ErrDataDecompressionFailed
}
defer r.Close()
// Limit decompressed size to 10MB to prevent DoS via decompression bomb
const maxDecompressedSize = 10 * 1024 * 1024
limitedReader := io.LimitReader(r, maxDecompressedSize+1) // +1 to detect overflow
n, err := io.Copy(&buf, limitedReader)
if err != nil {
log.Critical("message decompression failed: %v", err)
ErrDataDecompressionFailed.SetExtraError(err)
return ErrDataDecompressionFailed
}
if n > maxDecompressedSize {
log.Critical("decompressed data exceeds maximum size limit (%d bytes)", maxDecompressedSize)
ErrDataDecompressionFailed.SetExtraError(fmt.Errorf("decompressed size %d exceeds limit %d", n, maxDecompressedSize))
return ErrDataDecompressionFailed
}
ppd.BodyMessage = buf.Bytes() // separately allocated memory
//log.Debug("message decompressed %v -> %v", body, ppd.BodyMessage)
} else {
ppd.BodyMessage = append(ppd.BodyMessage, body...) // deep copy
}
return nil
}
func (ppd *PacketParserData) sendCookie() {
// Only NHP_SERVER reaches this path (the call site in validatePeer is
// gated on deviceType == NHP_SERVER). Server startup guarantees a
// signing key is installed — either operator-supplied for clusters,
// or randomly generated at process start for single-instance
// deployments — so an empty key here is a programmer error.View on GitHub (pinned to 6e04ca5ff0)