dotnet/aspnetcore · error · RuntimeException
The length header was incomplete
Error message
The length header was incomplete
What it means
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.
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.
Example fix
// before - parse a buffer that may be a partial frame
List<HubMessage> msgs = protocol.parseMessages(partialBuffer, binder);
// after - wait for a complete length prefix before parsing
while (buffer.remaining() >= 1 && needsMoreForVarInt(buffer)) { awaitMoreBytes(); }
List<HubMessage> msgs = protocol.parseMessages(buffer, binder); Defensive patterns
Strategy: validation
Validate before calling
// Ensure at least one full VarInt (up to 5 bytes) of framing is available
boolean hasFullVarInt(ByteBuffer b) {
int mark = b.position();
int n = 0;
try {
byte c;
do { if (!b.hasRemaining()) return false; c = b.get(); n++; }
while (n < 5 && (c & (byte)0x80) != 0);
return true;
} finally { ((java.nio.Buffer)b).position(mark); }
} Type guard
static boolean isFrameComplete(ByteBuffer b) {
return b != null && b.remaining() >= 1 && hasFullVarInt(b);
} Try / catch
try {
return Utils.readLengthHeader(buffer);
} catch (RuntimeException ex) {
if ("The length header was incomplete".equals(ex.getMessage())) {
// need to buffer more bytes from the transport; do not retry with the same buffer
throw new java.io.IOException("Partial frame - await more bytes", ex);
}
throw ex;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- MessagePack message was length
- Messages over 2GB in size are not supported
- Cannot read message size.
- Error reading length header.
- Error reading MessagePack data.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/6715761ea4f81605.
Report an issue: GitHub.
Appendix: source
Thrown at src/SignalR/clients/java/signalr/messagepack/src/main/java/com/microsoft/signalr/messagepack/Utils.java:37
// VarInt: 0x35 - %00110101 - the most significant bit is 0 so the value is %x0110101 i.e. 0x35 (53)
// VarInt: 0x80 0x25 - %10000000 %00101001 - the most significant bit of the first byte is 1 so the
// remaining bits (%x0000000) are the lowest bits of the value. The most significant bit of the second
// byte is 0 meaning this is last byte of the VarInt. The actual value bits (%x0101001) need to be
// prepended to the bits we already read so the values is %01010010000000 i.e. 0x1480 (5248)
// We support payloads up to 2GB so the biggest number we support is 7fffffff which when encoded as
// VarInt is 0xFF 0xFF 0xFF 0xFF 0x07 - hence the maximum length prefix is 5 bytes.
int length = 0;
int numBytes = 0;
int maxLength = 5;
byte curr;
do {
// If we run out of bytes before we finish reading the length header, the message is malformed
if (buffer.hasRemaining()) {
curr = buffer.get();
} else {
throw new RuntimeException("The length header was incomplete");
}
length = length | (curr & (byte) 0x7f) << (numBytes * 7);
numBytes++;
} while (numBytes < maxLength && (curr & (byte) 0x80) != 0);
// Max header length is 5, and the maximum value of the 5th byte is 0x07
if ((curr & (byte) 0x80) != 0 || (numBytes == maxLength && curr > (byte) 0x07)) {
throw new RuntimeException("Messages over 2GB in size are not supported");
}
return length;
}
public static ArrayList<Byte> getLengthHeader(int length) {
// This code writes length prefix of the message as a VarInt. Read the comment in
// the readLengthHeader for details.
ArrayList<Byte> header = new ArrayList<Byte>();View on GitHub (pinned to 3600ca084e)