dotnet/aspnetcore · error · Error

Expected a handshake response from the server.

Error message

Expected a handshake response from the server.

What it means

Thrown by HandshakeProtocol.parseHandshakeResponse when the parsed JSON object has a truthy 'type' field. A valid handshake response contains only { error?, minorVersion? } and no 'type'. A 'type' field indicates the server sent a regular HubMessage (e.g. an error or close) instead of the handshake response — meaning the handshake either failed or was already consumed, so the first message isn't the expected response.

Source

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

        } 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);
            remainingData = (textData.length > responseLength) ? textData.substring(responseLength) : null;
        }

        // At this point we should have just the single handshake message
        const messages = TextMessageFormat.parse(messageData);
        const response = JSON.parse(messages[0]);
        if (response.type) {
            throw new Error("Expected a handshake response from the server.");
        }
        const responseMessage: HandshakeResponseMessage = response;

        // multiple messages could have arrived with handshake
        // return additional data to be parsed as usual, or null if all parsed
        return [remainingData, responseMessage];
    }
}

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Verify the protocol name passed to withHubProtocol matches a protocol the server supports.
  2. Ensure the protocol version is compatible between client and server.
  3. Check the server logs for handshake negotiation errors (common: 404 on the hub endpoint, or protocol not configured).
  4. Confirm you are not calling parseHandshakeResponse twice on the same data stream.

Example fix

// before
const builder = new signalR.HubConnectionBuilder()
  .withUrl('/hub')
  .withHubProtocol(new signalR.JsonHubProtocol());
// server only supports messagepack -> typed error -> 'Expected a handshake response from the server.'

// after
const builder = new signalR.HubConnectionBuilder()
  .withUrl('/hub')
  .withHubProtocol(new MessagePackHubProtocol()); // match what the server expects
Defensive patterns

Strategy: validation

Validate before calling

// after parsing the JSON, validate it looks like a handshake response
function isHandshakeResponse(parsed: any): boolean {
  return parsed && !parsed.type && (parsed.error === undefined || typeof parsed.error === 'string');
}
const parsed = JSON.parse(messages[0]);
if (!isHandshakeResponse(parsed)) {
  throw new Error('Server did not send a handshake response; check protocol name/version');
}

Type guard

function isHandshakeResponseMessage(v: unknown): v is { error?: string; minorVersion?: number } {
  return typeof v === 'object' && v !== null && !('type' in v);
}

Try / catch

try {
  const [remaining, resp] = handshakeProtocol.parseHandshakeResponse(data);
} catch (e) {
  if (e.message === 'Expected a handshake response from the server.') {
    // server likely rejected the protocol/version; surface a clear config error
    throw new Error('Handshake failed: ensure the hub protocol and version match the server');
  }
  throw e;
}

Prevention

When it happens

Trigger: The server rejects the protocol and sends a typed error/close message before the handshake completes; the handshake response was already parsed and a subsequent hub message is being fed to parseHandshakeResponse; server-side handshake negotiation failed (unknown protocol/version).

Common situations: Client requests a protocol the server doesn't support (e.g. 'messagepack' on a JSON-only server); protocol version mismatch; the server sends an error message with a type field when it fails to negotiate; reconnect logic double-parsing the handshake.

Understand the failure class

Related errors


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