{"record":{"id":"468a038e6c1aa921","repo":"dotnet/aspnetcore","slug":"expected-a-handshake-response-from-the-server","errorCode":null,"errorMessage":"Expected a handshake response from the server.","messagePattern":"Expected a handshake response from the server\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/ts/signalr/src/HandshakeProtocol.ts","lineNumber":61,"sourceCode":"        } else {\n            const textData: string = data;\n            const separatorIndex = textData.indexOf(TextMessageFormat.RecordSeparator);\n            if (separatorIndex === -1) {\n                throw new Error(\"Message is incomplete.\");\n            }\n\n            // content before separator is handshake response\n            // optional content after is additional messages\n            const responseLength = separatorIndex + 1;\n            messageData = textData.substring(0, responseLength);\n            remainingData = (textData.length > responseLength) ? textData.substring(responseLength) : null;\n        }\n\n        // At this point we should have just the single handshake message\n        const messages = TextMessageFormat.parse(messageData);\n        const response = JSON.parse(messages[0]);\n        if (response.type) {\n            throw new Error(\"Expected a handshake response from the server.\");\n        }\n        const responseMessage: HandshakeResponseMessage = response;\n\n        // multiple messages could have arrived with handshake\n        // return additional data to be parsed as usual, or null if all parsed\n        return [remainingData, responseMessage];\n    }\n}\n","sourceCodeStart":43,"sourceCodeEnd":70,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/SignalR/clients/ts/signalr/src/HandshakeProtocol.ts#L43-L70","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nconst builder = new signalR.HubConnectionBuilder()\n  .withUrl('/hub')\n  .withHubProtocol(new signalR.JsonHubProtocol());\n// server only supports messagepack -> typed error -> 'Expected a handshake response from the server.'\n\n// after\nconst builder = new signalR.HubConnectionBuilder()\n  .withUrl('/hub')\n  .withHubProtocol(new MessagePackHubProtocol()); // match what the server expects","handlingStrategy":"validation","validationCode":"// after parsing the JSON, validate it looks like a handshake response\nfunction isHandshakeResponse(parsed: any): boolean {\n  return parsed && !parsed.type && (parsed.error === undefined || typeof parsed.error === 'string');\n}\nconst parsed = JSON.parse(messages[0]);\nif (!isHandshakeResponse(parsed)) {\n  throw new Error('Server did not send a handshake response; check protocol name/version');\n}","typeGuard":"function isHandshakeResponseMessage(v: unknown): v is { error?: string; minorVersion?: number } {\n  return typeof v === 'object' && v !== null && !('type' in v);\n}","tryCatchPattern":"try {\n  const [remaining, resp] = handshakeProtocol.parseHandshakeResponse(data);\n} catch (e) {\n  if (e.message === 'Expected a handshake response from the server.') {\n    // server likely rejected the protocol/version; surface a clear config error\n    throw new Error('Handshake failed: ensure the hub protocol and version match the server');\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["handshake","protocol-negotiation","version-skew","json","configuration"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}