dotnet/aspnetcore · error · RuntimeException

Error reading length header.

Error message

Error reading length header.

What it means

Utils.readLengthHeader reads the length prefix and may throw IOException for malformed prefix bytes or insufficient data. MessagePackHubProtocol catches that IOException and wraps it as a RuntimeException with 'Error reading length header.' This is the entry-point parse failure: the framing could not even be read.

Solutions

  1. Confirm both server and client negotiate the same protocol - if the server only has JSON, do not call withHubProtocol(MessagePackHubProtocol).
  2. On the server register AddMessagePackProtocol() if you intend to use MessagePack; on the client ensure the negotiated protocol is MessagePack.
  3. Inspect the first few bytes of the failing frame to confirm they look like a MessagePack length header (varint).

Example fix

// before - client forces MessagePack but server has only JSON
HubConnectionBuilder.create(url)
    .withHubProtocol(new MessagePackHubProtocol())
    .build();

// after - register MessagePack on the server too
// server: services.AddSignalR().AddMessagePackProtocol();
HubConnectionBuilder.create(url)
    .withHubProtocol(new MessagePackHubProtocol())
    .build();
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the negotiated protocol matches what you configured.
// After start(), inspect handshake result / server response to ensure
// 'messagepack' was accepted (not silently falling back to JSON).
// Pre-flight: hit /negotiate and check that the server lists messagepack.
Set<String> protocols = fetchNegotiate(url).availableTransports
    .stream().flatMap(t -> t.getTransferFormats().stream())
    .collect(toSet()); // adjust to your server's negotiate shape
if (!protocols.contains("messagepack")) {
  throw new IllegalStateException("Server does not offer MessagePack");
}

Try / catch

hub.onClosed(ex -> {
  if (ex != null && ex.getMessage().contains("Error reading length header")) {
    // protocol mismatch; verify AddMessagePackProtocol on server
  }
});

Prevention

When it happens

Trigger: The first bytes of a MessagePack frame are not a valid length-header encoding, or the buffer has fewer than the header byte count; the protocol received binary data that is not MessagePack at all.

Common situations: Hub endpoint configured with JSON on the server but the client selected MessagePack (or vice-versa); a custom subprotocol mismatched the wire format; binary corruption from a transport layer; a stale buffer reused after a reconnect.

Related errors


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

Appendix: source

Thrown at src/SignalR/clients/java/signalr/messagepack/src/main/java/com/microsoft/signalr/messagepack/MessagePackHubProtocol.java:86

         // MessagePack library can't handle read-only ByteBuffer - copy into an array-backed ByteBuffer if this is the case
        if (payload.isReadOnly()) {
            byte[] payloadBytes = new byte[payload.remaining()];
            payload.get(payloadBytes, 0, payloadBytes.length);
            payload = ByteBuffer.wrap(payloadBytes);
        }

        List<HubMessage> hubMessages = new ArrayList<>();

        while (payload.hasRemaining()) {
            int length;
            try {
                length = Utils.readLengthHeader(payload);
                // Throw if remaining buffer is shorter than length header
                if (payload.remaining() < length) {
                    throw new RuntimeException(String.format("MessagePack message was length %d but claimed to be length %d.", payload.remaining(), length));
                }
            } catch (IOException ex) {
                throw new RuntimeException("Error reading length header.", ex);
            }
            // Instantiate MessageUnpacker
            try(MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(payload)) {

                int itemCount = unpacker.unpackArrayHeader();
                HubMessageType messageType = HubMessageType.values()[unpacker.unpackInt() - 1];

                switch (messageType) {
                    case INVOCATION:
                        hubMessages.add(createInvocationMessage(unpacker, binder, itemCount, payload));
                        break;
                    case STREAM_ITEM:
                        hubMessages.add(createStreamItemMessage(unpacker, binder, payload));
                        break;
                    case COMPLETION:
                        hubMessages.add(createCompletionMessage(unpacker, binder, payload));
                        break;
                    case STREAM_INVOCATION:

View on GitHub (pinned to 3600ca084e)