dotnet/aspnetcore · error · RuntimeException

Messages over 2GB in size are not supported

Error message

Messages over 2GB in size are not supported

What it means

readLengthHeader caps the supported payload size at 2 GB (Integer.MAX_VALUE, 0x7FFFFFFF). After reading up to 5 VarInt bytes, if the continuation bit is still set or the 5th byte exceeds 0x07, the decoded length would overflow a signed int, so the frame is rejected.

Solutions

  1. Treat as a malformed/fatal frame: drop the connection and reconnect.
  2. If you actually stream large data, chunk it through SignalR streaming (StreamItem) instead of one huge message.
  3. Add an inbound size cap before invoking readLengthHeader to reject absurd values early.

Example fix

// before - parse untrusted buffer directly
int len = Utils.readLengthHeader(buffer);
// after - sanity-cap first
int len = Utils.readLengthHeader(buffer);
if (len < 0 || len > MAX_ALLOWED) throw new IOException("Frame too large: " + len);
Defensive patterns

Strategy: validation

Validate before calling

final int MAX_FRAME = 64 * 1024 * 1024; // pick a sane cap
int len = Utils.readLengthHeader(buffer);
if (len < 0 || len > MAX_FRAME) {
    throw new java.io.IOException("Rejecting oversize/corrupt frame length: " + len);
}

Type guard

static boolean isPlausibleLength(int len) {
    return len >= 0 && len <= MAX_FRAME;
}

Try / catch

try {
    int len = Utils.readLengthHeader(buffer);
} catch (RuntimeException ex) {
    if (ex.getMessage().contains("over 2GB")) {
        // corrupt length header or malicious input - drop connection
        connection.dispose();
    }
    throw ex;
}

Prevention

When it happens

Trigger: The VarInt length prefix decodes to a value larger than 2 GiB, either because the payload genuinely exceeds that size or - far more commonly - because the length bytes are corrupt and decode to a huge number (Utils.java:43-46).

Common situations: Corrupted framing where random bytes with the high bit set are interpreted as a multi-byte length; a peer injecting garbage; rarely, an attempt to send a legitimately enormous payload.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/c23c91602b1b058b. Report an issue: GitHub.

Appendix: source

Thrown at src/SignalR/clients/java/signalr/messagepack/src/main/java/com/microsoft/signalr/messagepack/Utils.java:45

        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>();
        do {
            byte curr = (byte) (length & 0x7f);
            length >>= 7;
            if (length > 0) {
                curr |= 0x80;
            }
            header.add(curr);
        } while (length > 0);

View on GitHub (pinned to 3600ca084e)