{"record":{"id":"39ea5795d7363e41","repo":"OpenNHP/opennhp","slug":"packet-too-large","errorCode":null,"errorMessage":"packet too large","messagePattern":"packet too large","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"endpoints/relay/relay.go","lineNumber":969,"sourceCode":"\t\thttp.Error(w, errMsg, status)\n\t\treturn\n\t}\n\n\t// Read inner NHP packet from request body. Cap at maxPacketSize+1 so we\n\t// can reject oversize bodies without pulling an unbounded amount into\n\t// memory. A single r.Body.Read() is not guaranteed to return the full\n\t// payload; io.ReadAll drains until EOF.\n\tinnerPacket, err := io.ReadAll(io.LimitReader(r.Body, int64(maxPacketSize)+1))\n\tif err != nil {\n\t\thttp.Error(w, \"failed to read body\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif len(innerPacket) == 0 {\n\t\thttp.Error(w, \"empty packet\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif len(innerPacket) > maxPacketSize {\n\t\thttp.Error(w, \"packet too large\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tn := len(innerPacket)\n\n\t// Extract the counter from the inner packet header (bytes [16:24], big-endian uint64).\n\t// The NHP server echoes this counter in its ACK/COK response, so we use it\n\t// to match the response back to this HTTP request.\n\tif n < 24 {\n\t\thttp.Error(w, \"inner packet too short\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tinnerCounter := binary.BigEndian.Uint64(innerPacket[16:24])\n\n\t// Extract real client address before picking an instance so sticky\n\t// sessions can hash on it.\n\trealAddr, err := realClientAddr(r)\n\tif err != nil {\n\t\tlog.Error(\"[Relay] %v\", err)","sourceCodeStart":951,"sourceCodeEnd":987,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/relay/relay.go#L951-L987","documentation":"handleRelay responds with HTTP 400 'packet too large' when the body exceeds maxPacketSize bytes. The read is capped at maxPacketSize+1 so anything longer than the limit is detected without buffering an unbounded payload, protecting relay memory from oversized uploads.","triggerScenarios":"POSTing a body larger than maxPacketSize to the relay endpoint, as exercised by TestRouting_OversizeBodyReturns400.","commonSituations":"Client serializing a malformed or bloated packet; a misconfigured client using a packet format/version with a larger header; generic HTTP clients reusing the endpoint to upload unrelated data.","solutions":["Reduce the inner packet size on the client to fit within maxPacketSize","Check for version skew: client and relay must agree on the NHP packet size limit","Verify the client is sending the compact NHP packet, not a wrapped/base64 or debug-encoded form"],"exampleFix":"// before\nbody := buildHugePayload() // > maxPacketSize\n// after\nif len(body) > maxPacketSize { return fmt.Errorf(\"packet %d exceeds limit %d\", len(body), maxPacketSize) }\nreq, _ := http.NewRequest(\"POST\", relayURL, bytes.NewReader(body))","handlingStrategy":"validation","validationCode":"if len(packet) > maxPacketSize {\n    return fmt.Errorf(\"packet is %d bytes, exceeds relay limit %d\", len(packet), maxPacketSize)\n}","typeGuard":null,"tryCatchPattern":"if resp.StatusCode == http.StatusBadRequest {\n    b, _ := io.ReadAll(resp.Body)\n    if strings.Contains(string(b), \"packet too large\") {\n        return fmt.Errorf(\"oversize packet %d bytes; compress or split before sending\", len(packet))\n    }\n}","preventionTips":["Enforce maxPacketSize client-side before sending","Keep client packet-size limits in sync with relay version","Avoid base64/verbose encodings that inflate the payload"],"tags":["http","validation","size-limit"],"backgroundTag":"payload-too-large","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"}