dotnet/aspnetcore · error · Error

Message is incomplete.

Error message

Message is incomplete.

What it means

Thrown by HandshakeProtocol.parseHandshakeResponse in the binary branch when the ArrayBuffer does not contain a RecordSeparator (0x1e) byte. The handshake response is JSON text terminated by a record separator, and for binary protocols it arrives as the first chunk of an ArrayBuffer. Absence of the separator means the handshake response hasn't fully arrived or is malformed.

Source

Thrown at src/SignalR/clients/ts/signalr/src/HandshakeProtocol.ts:35

}

/** @private */
export class HandshakeProtocol {
    // Handshake request is always JSON
    public writeHandshakeRequest(handshakeRequest: HandshakeRequestMessage): string {
        return TextMessageFormat.write(JSON.stringify(handshakeRequest));
    }

    public parseHandshakeResponse(data: any): [any, HandshakeResponseMessage] {
        let messageData: string;
        let remainingData: any;

        if (isArrayBuffer(data)) {
            // Format is binary but still need to read JSON text from handshake response
            const binaryData = new Uint8Array(data);
            const separatorIndex = binaryData.indexOf(TextMessageFormat.RecordSeparatorCode);
            if (separatorIndex === -1) {
                throw new Error("Message is incomplete.");
            }

            // content before separator is handshake response
            // optional content after is additional messages
            const responseLength = separatorIndex + 1;
            messageData = String.fromCharCode.apply(null, Array.prototype.slice.call(binaryData.slice(0, responseLength)));
            remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null;
        } else {
            const textData: string = data;
            const separatorIndex = textData.indexOf(TextMessageFormat.RecordSeparator);
            if (separatorIndex === -1) {
                throw new Error("Message is incomplete.");
            }

            // content before separator is handshake response
            // optional content after is additional messages
            const responseLength = separatorIndex + 1;
            messageData = textData.substring(0, responseLength);

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Buffer incoming bytes until a 0x1e separator is present before calling parseHandshakeResponse.
  2. Confirm the server URL points to a real SignalR hub endpoint.
  3. Check server logs — if it crashed or rejected the protocol it may never send a valid handshake response.
  4. Verify the negotiated protocol name is spelled correctly on the client.

Example fix

// before
const [remaining, resp] = handshakeProtocol.parseHandshakeResponse(incompleteBuffer);

// after
// accumulate until the record separator byte (0x1e) is present
if (new Uint8Array(buffer).indexOf(0x1e) === -1) {
  // wait for more data
  return;
}
const [remaining, resp] = handshakeProtocol.parseHandshakeResponse(buffer);
Defensive patterns

Strategy: validation

Validate before calling

// buffer until the record separator (0x1e) is present (binary path)
const SEP = 0x1e;
function handshakeBufferComplete(buffer: ArrayBuffer): boolean {
  return new Uint8Array(buffer).indexOf(SEP) !== -1;
}
if (!handshakeBufferComplete(buffer)) {
  // wait for more data
  return;
}
const [remaining, resp] = handshakeProtocol.parseHandshakeResponse(buffer);

Try / catch

try {
  const [remaining, resp] = handshakeProtocol.parseHandshakeResponse(buffer);
} catch (e) {
  if (e.message === 'Message is incomplete.') {
    // accumulate more bytes and retry once the separator arrives
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: The binary handshake chunk delivered to parseHandshakeResponse is incomplete (separator not yet received); a chunk that is actually a later message, not the handshake; the server never sent a proper handshake response.

Common situations: A transport delivered a partial first frame (the separator is in the next chunk); network latency splitting the handshake; a non-SignalR endpoint answering the WebSocket; server-side error closing the connection before sending the handshake response.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/4594a02506260070. Report an issue: GitHub.