{"record":{"id":"6715761ea4f81605","repo":"dotnet/aspnetcore","slug":"the-length-header-was-incomplete","errorCode":null,"errorMessage":"The length header was incomplete","messagePattern":"The length header was incomplete","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/java/signalr/messagepack/src/main/java/com/microsoft/signalr/messagepack/Utils.java","lineNumber":37,"sourceCode":"        // VarInt: 0x35 - %00110101 - the most significant bit is 0 so the value is %x0110101 i.e. 0x35 (53)\n        // VarInt: 0x80 0x25 - %10000000 %00101001 - the most significant bit of the first byte is 1 so the\n        // remaining bits (%x0000000) are the lowest bits of the value. The most significant bit of the second\n        // byte is 0 meaning this is last byte of the VarInt. The actual value bits (%x0101001) need to be\n        // prepended to the bits we already read so the values is %01010010000000 i.e. 0x1480 (5248)\n        // We support payloads up to 2GB so the biggest number we support is 7fffffff which when encoded as\n        // VarInt is 0xFF 0xFF 0xFF 0xFF 0x07 - hence the maximum length prefix is 5 bytes.\n        \n        int length = 0;\n        int numBytes = 0;\n        int maxLength = 5;\n        byte curr;\n        \n        do {\n            // If we run out of bytes before we finish reading the length header, the message is malformed\n            if (buffer.hasRemaining()) {\n                curr = buffer.get();\n            } else {\n                throw new RuntimeException(\"The length header was incomplete\");\n            }\n            length = length | (curr & (byte) 0x7f) << (numBytes * 7);\n            numBytes++;\n        } while (numBytes < maxLength && (curr & (byte) 0x80) != 0);\n        \n        // Max header length is 5, and the maximum value of the 5th byte is 0x07\n        if ((curr & (byte) 0x80) != 0 || (numBytes == maxLength && curr > (byte) 0x07)) {\n            throw new RuntimeException(\"Messages over 2GB in size are not supported\");\n        }\n        \n        return length;\n    }\n    \n    public static ArrayList<Byte> getLengthHeader(int length) {\n        // This code writes length prefix of the message as a VarInt. Read the comment in\n        // the readLengthHeader for details.\n        \n        ArrayList<Byte> header = new ArrayList<Byte>();","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/java/signalr/messagepack/src/main/java/com/microsoft/signalr/messagepack/Utils.java#L19-L55","documentation":"Utils.readLengthHeader reads the VarInt length prefix from the framing buffer. If the buffer has no more bytes before the VarInt terminates (continuation bit clear or the 5-byte cap reached), the prefix is incomplete and the message is considered malformed.","triggerScenarios":"A truncated/partial frame arrives: the connection delivered fewer bytes than a complete VarInt length header requires (Utils.java:32-38). This is framing-level corruption, not a value-level error.","commonSituations":"Network truncation or a partial read, an over-aggressive buffer slice, a proxy/Load Balancer cutting the stream, or a peer sending non-SignalR bytes that happen to have the continuation bit set then stop.","solutions":["Accumulate more bytes before parsing - the framing layer should wait until at least one full VarInt (up to 5 bytes) is available.","Treat as a fatal transport error: close and reconnect the SignalR connection.","Verify the transport (WebSockets/LongPolling) is intact and not being truncated by an intermediary."],"exampleFix":"// before - parse a buffer that may be a partial frame\nList<HubMessage> msgs = protocol.parseMessages(partialBuffer, binder);\n// after - wait for a complete length prefix before parsing\nwhile (buffer.remaining() >= 1 && needsMoreForVarInt(buffer)) { awaitMoreBytes(); }\nList<HubMessage> msgs = protocol.parseMessages(buffer, binder);","handlingStrategy":"validation","validationCode":"// Ensure at least one full VarInt (up to 5 bytes) of framing is available\nboolean hasFullVarInt(ByteBuffer b) {\n    int mark = b.position();\n    int n = 0;\n    try {\n        byte c;\n        do { if (!b.hasRemaining()) return false; c = b.get(); n++; }\n        while (n < 5 && (c & (byte)0x80) != 0);\n        return true;\n    } finally { ((java.nio.Buffer)b).position(mark); }\n}","typeGuard":"static boolean isFrameComplete(ByteBuffer b) {\n    return b != null && b.remaining() >= 1 && hasFullVarInt(b);\n}","tryCatchPattern":"try {\n    return Utils.readLengthHeader(buffer);\n} catch (RuntimeException ex) {\n    if (\"The length header was incomplete\".equals(ex.getMessage())) {\n        // need to buffer more bytes from the transport; do not retry with the same buffer\n        throw new java.io.IOException(\"Partial frame - await more bytes\", ex);\n    }\n    throw ex;\n}","preventionTips":["Buffer transport bytes until a complete length prefix is present before parsing.","Treat repeated partial-frame errors as a connection problem and reconnect.","Verify intermediaries (proxies, load balancers) are not truncating frames."],"tags":["java","signalr","messagepack","framing","network"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}