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

  1. Reduce the message size on the sending side — chunk or split the payload into multiple NHP messages so each decompressed body fits the limit.
  2. If the traffic is trusted and legitimately large, raise maxDecompressedSize in the responder configuration and ensure adequate memory headroom.
  3. Verify both peers run compatible versions with matching compression/size-limit settings.
  4. 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

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


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)