{"record":{"id":"96d2aa4cd5cb8681","repo":"dotnet/aspnetcore","slug":"message-is-incomplete-96d2aa","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/TextMessageFormat.ts","lineNumber":16,"sourceCode":"﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\n// Not exported from index\n/** @private */\nexport class TextMessageFormat {\n    public static RecordSeparatorCode = 0x1e;\n    public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);\n\n    public static write(output: string): string {\n        return `${output}${TextMessageFormat.RecordSeparator}`;\n    }\n\n    public static parse(input: string): string[] {\n        if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) {\n            throw new Error(\"Message is incomplete.\");\n        }\n\n        const messages = input.split(TextMessageFormat.RecordSeparator);\n        messages.pop();\n        return messages;\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":24,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/ts/signalr/src/TextMessageFormat.ts#L1-L24","documentation":"Thrown by TextMessageFormat.parse when the input string does not end with the RecordSeparator character (U+001E). SignalR's text protocol frames every message with a trailing 0x1E so multiple messages can be batched in one stream; this guard rejects a partial/unterminated frame before splitting. It is a low-level framing invariant used by JsonHubProtocol and the handshake decoder, not an API the application calls directly.","triggerScenarios":"The parse() method receives a string whose last character is not String.fromCharCode(0x1e). This happens when a WebSocket/Long-polling chunk delivers a fragment of a message, when a custom transport delivers bytes before the full frame arrives, or when test fixtures feed an unframed JSON string into the protocol layer.","commonSituations":"Buffering logic upstream was removed or bypassed; a partial read raced ahead of the framing buffer; a server/proxy stripped control characters; unit tests pass raw JSON without appending the record separator.","solutions":["Ensure the transport's receive path feeds complete frames to the protocol (the built-in WebSocket/LongPolling/SSE transports already buffer correctly - prefer them over a custom transport).","If you call TextMessageFormat.parse directly in tests, append TextMessageFormat.RecordSeparator to each input message before parsing.","Inspect the raw bytes just before the throw: a missing trailing 0x1E usually points to a truncated chunk from the network layer or a proxy mangling binary/text control chars.","Upgrade the client to the version matching your server; framing rules are stable but custom forks sometimes drop the separator."],"exampleFix":"// before (test/fixture)\nconst msgs = TextMessageFormat.parse('{\"type\":6}'); // throws\n\n// after\nconst rs = String.fromCharCode(0x1e);\nconst msgs = TextMessageFormat.parse('{\"type\":6}' + rs);","handlingStrategy":"validation","validationCode":"function isCompleteTextFrame(input: string): boolean {\n  return input.length > 0 && input[input.length - 1] === String.fromCharCode(0x1e);\n}\n\n// before calling TextMessageFormat.parse:\nif (!isCompleteTextFrame(raw)) {\n  // buffer more bytes or surface a framing error; do not call parse\n}","typeGuard":"function isCompleteTextFrame(input: unknown): input is string {\n  return typeof input === 'string'\n    && input.length > 0\n    && input.charCodeAt(input.length - 1) === 0x1e;\n}","tryCatchPattern":"try {\n  const messages = TextMessageFormat.parse(buffer);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Message is incomplete.') {\n    // keep buffering; this chunk was partial\n  } else throw e;\n}","preventionTips":["Prefer built-in transports (WebSocket/LongPolling/SSE) - they buffer complete frames before invoking the protocol.","In tests, always append String.fromCharCode(0x1e) to each message you feed to parse.","Never feed raw JSON to TextMessageFormat.parse; the framing byte is mandatory."],"tags":["signalr","typescript","protocol","framing","transport"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}