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
- Verify the protocol name passed to withHubProtocol matches a protocol the server supports.
- Ensure the protocol version is compatible between client and server.
- Check the server logs for handshake negotiation errors (common: 404 on the hub endpoint, or protocol not configured).
- 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
- Ensure withHubProtocol uses a name the server supports (json vs messagepack).
- Keep client and server SignalR versions compatible.
- Check server logs for handshake negotiation failures (404, unsupported protocol).
- Do not feed a subsequent hub message into parseHandshakeResponse.
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Circuit options have already been configured.
- WebAssembly options have already been configured.
- For a ${resourceType} resource, custom loaders must supply a
- EqualTo validator requires a non-empty "other" parameter.
- FileExtensions validator requires a non-empty "extensions" p
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/468a038e6c1aa921.
Report an issue: GitHub.