{"record":{"id":"4594a02506260070","repo":"dotnet/aspnetcore","slug":"message-is-incomplete","errorCode":null,"errorMessage":"Message is incomplete.","messagePattern":"Message is incomplete\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/ts/signalr/src/HandshakeProtocol.ts","lineNumber":35,"sourceCode":"}\n\n/** @private */\nexport class HandshakeProtocol {\n    // Handshake request is always JSON\n    public writeHandshakeRequest(handshakeRequest: HandshakeRequestMessage): string {\n        return TextMessageFormat.write(JSON.stringify(handshakeRequest));\n    }\n\n    public parseHandshakeResponse(data: any): [any, HandshakeResponseMessage] {\n        let messageData: string;\n        let remainingData: any;\n\n        if (isArrayBuffer(data)) {\n            // Format is binary but still need to read JSON text from handshake response\n            const binaryData = new Uint8Array(data);\n            const separatorIndex = binaryData.indexOf(TextMessageFormat.RecordSeparatorCode);\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 = String.fromCharCode.apply(null, Array.prototype.slice.call(binaryData.slice(0, responseLength)));\n            remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null;\n        } 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);","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/ts/signalr/src/HandshakeProtocol.ts#L17-L53","documentation":"While parsing a binary handshake response, HandshakeProtocol looks for the RecordSeparator byte (0x1E) in the incoming Uint8Array. If indexOf returns -1, the buffer does not contain a complete handshake frame, so the message is incomplete and cannot be parsed.","triggerScenarios":"parseHandshakeResponse receives an ArrayBuffer that lacks the 0x1E terminator. Happens when the transport delivered a partial frame (TCP fragmentation mid-handshake), when a binary protocol is fed a non-SignalR payload, or when the underlying transport's onreceive callback hands off an incomplete chunk.","commonSituations":"Slow or lossy network where the first chunk of the handshake arrives split. A proxy buffering or re-chunking the response. Mismatch between client transport expectation (binary) and what the server actually sent (text). Connecting to a non-SignalR binary endpoint.","solutions":["Confirm the endpoint is an ASP.NET Core SignalR hub speaking the binary protocol.","If you are buffering manually, ensure you accumulate bytes until the 0x1E separator arrives before calling parseHandshakeResponse.","Retry the connection; transient fragmentation often resolves on reconnect.","Inspect the raw incoming bytes (log message content) to verify the separator is missing vs. the data being garbage."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"function hasRecordSeparator(bytes: Uint8Array): boolean {\n  return bytes.indexOf(0x1e) !== -1;\n}\n// accumulate until separator present before handing off to the protocol","typeGuard":"function isCompleteBinaryHandshake(bytes: Uint8Array): boolean {\n  return bytes.indexOf(0x1e) !== -1;\n}","tryCatchPattern":"import { HandshakeProtocol } from \"@microsoft/signalr\";\nasync function startWithRetry(conn: signalR.HubConnection, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try { await conn.start(); return; }\n    catch (e) {\n      if (e instanceof Error && e.message === \"Message is incomplete.\" && i < attempts - 1) {\n        await new Promise(r => setTimeout(r, 500 * (i + 1)));\n        continue;\n      }\n      throw e;\n    }\n  }\n}","preventionTips":["Enable automatic reconnects (.withAutomaticReconnect()) so transient fragmentation self-heals.","If buffering manually, accumulate bytes until 0x1E arrives.","Confirm the endpoint speaks the binary SignalR protocol."],"tags":["handshake","binary","transport","framing"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}