OpenNHP/opennhp · error
inner packet too short
Error message
inner packet too short
What it means
handleRelay responds with HTTP 400 'inner packet too short' when the body is non-empty and within the size cap but shorter than 24 bytes, which is the minimum needed to read the big-endian uint64 counter at bytes [16:24]. The counter is required to match the NHP server's ACK/COK response back to this HTTP request, so short payloads cannot be processed.
Solutions
- Verify the client serializes the full NHP packet header (at least 24 bytes) before sending
- Check the packet-construction code for premature writes or truncation (e.g. wrong buffer size)
- Confirm client and relay use the same NHP packet version/header layout
Example fix
// before
req, _ := http.NewRequest("POST", relayURL, bytes.NewReader(packet[:10]))
// after
if len(packet) < 24 { return errors.New("packet truncated") }
req, _ := http.NewRequest("POST", relayURL, bytes.NewReader(packet)) Defensive patterns
Strategy: validation
Validate before calling
if len(packet) < 24 {
return fmt.Errorf("packet truncated: %d bytes, need >= 24", len(packet))
} Try / catch
if resp.StatusCode == http.StatusBadRequest {
b, _ := io.ReadAll(resp.Body)
if strings.Contains(string(b), "inner packet too short") {
return fmt.Errorf("packet builder produced only %d bytes; check header serialization", len(packet))
}
} Prevention
- Validate the 24-byte minimum header client-side before POSTing
- Pin client and relay to the same NHP packet version
- Test the packet builder output length in unit tests
When it happens
Trigger: POSTing a body of 1-23 bytes to the relay endpoint, as exercised by TestRouting_ShortBodyReturns400.
Common situations: Truncated packet from a buggy serializer; client sending only a partial header; garbage/garbled test payloads; packet built for a different protocol version with a smaller header layout.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- empty packet
- packet too large
- cluster : missing publicKeyBase64
- cluster ( ): no instances configured
- cluster instance # : must set either Host or Ip
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/1ce0b500ad82b62e.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/relay/relay.go:978
if err != nil {
http.Error(w, "failed to read body", http.StatusBadRequest)
return
}
if len(innerPacket) == 0 {
http.Error(w, "empty packet", http.StatusBadRequest)
return
}
if len(innerPacket) > maxPacketSize {
http.Error(w, "packet too large", http.StatusBadRequest)
return
}
n := len(innerPacket)
// Extract the counter from the inner packet header (bytes [16:24], big-endian uint64).
// The NHP server echoes this counter in its ACK/COK response, so we use it
// to match the response back to this HTTP request.
if n < 24 {
http.Error(w, "inner packet too short", http.StatusBadRequest)
return
}
innerCounter := binary.BigEndian.Uint64(innerPacket[16:24])
// Extract real client address before picking an instance so sticky
// sessions can hash on it.
realAddr, err := realClientAddr(r)
if err != nil {
log.Error("[Relay] %v", err)
http.Error(w, "relay misconfigured: missing X-Real-IP header from local reverse proxy", http.StatusBadGateway)
return
}
realAddrKey := realAddr.String()
// Pick a target instance. When StickyInstance is enabled,
// hash the real client IP so the same client always reaches the same
// instance — required for stateful flows like OTP→REG where per-
// instance local state (SQLite) must be consistent across requests.View on GitHub (pinned to 6e04ca5ff0)