OrchardCMS/OrchardCore · error · Error
Expected a handshake response from the server.
Error message
Expected a handshake response from the server.
What it means
After parsing the handshake response JSON, the client verifies it is an actual handshake reply by checking that it has no `type` field; handshake responses are the only SignalR messages without one. If `type` is present, the first message received was a regular hub message (invocation, close, ping) rather than a handshake reply, so the client throws 'Expected a handshake response from the server.' This indicates protocol misuse or a desynchronized stream.
Solutions
- Ensure the server responds to the handshake request with `{}` (no type field) framed with 0x1E before any hub messages
- Complete the client handshake before the server sends any hub invocations or pings
- Check for message replay/buffering in proxies and disable it for SignalR connections
- If writing a custom server, follow the SignalR handshake spec: first outbound message must be the handshake response
Example fix
// before (custom server)
await ws.send(JSON.stringify({ type: 1, target: 'init' }) + '\u001e'); // sent before handshake reply
// after
await ws.send('{}\u001e'); // handshake response first
await ws.send(JSON.stringify({ type: 1, target: 'init' }) + '\u001e'); Defensive patterns
Strategy: try-catch
Validate before calling
const parsed = JSON.parse(firstMessage); if (parsed && typeof parsed.type === 'number') fail('first message must be handshake response'); Type guard
const isHandshakeResponse = (m) => m !== null && typeof m === 'object' && m.type === undefined;
Try / catch
try { await connection.start(); } catch (e) { if (e.message.includes('Expected a handshake response')) { inspectServerHandshake(); } else throw e; } Prevention
- Server must send {} + 0x1E as its first message
- Never send hub messages before handshake completion
- Disable proxy message replay for SignalR paths
When it happens
Trigger: Server sends an invocation/ping before completing the handshake; a proxy replays buffered messages out of order; a custom server implementation responds to the handshake with a typed message; connecting mid-stream to a persistent connection already delivering data.
Common situations: Custom SignalR server/bridge implementations; message replay from a misbehaving load balancer; mixing handshake and message pipelines in hand-rolled protocol code; stale long-poll connections delivering queued messages after reconnect.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Message is incomplete.
- The ClamAV antivirus scanner returned an unexpected…
- The ' ' argument is required.
- The ' ' argument should not be empty.
- Unknown value: .
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/ea5c6015c1357acc.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.SignalR/wwwroot/Scripts/signalr.js:902
remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null;
}
else {
const textData = 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 = response;
// multiple messages could have arrived with handshake
// return additional data to be parsed as usual, or null if all parsed
return [remainingData, responseMessage];
}
}
;// CONCATENATED MODULE: ./src/IHubProtocol.ts
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
/** Defines the type of a Hub Message. */
var MessageType;
(function (MessageType) {
/** Indicates the message is an Invocation message and implements the {@link @microsoft/signalr.InvocationMessage} interface. */
MessageType[MessageType["Invocation"] = 1] = "Invocation";
/** Indicates the message is a StreamItem message and implements the {@link @microsoft/signalr.StreamItemMessage} interface. */
MessageType[MessageType["StreamItem"] = 2] = "StreamItem";View on GitHub (pinned to 4306c0717f)