{"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/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/SignalR/clients/ts/signalr/src/TextMessageFormat.ts#L1-L24","documentation":"SignalR's text framing terminates every record with the ASCII Record Separator (0x1e). TextMessageFormat.parse rejects input whose final character is not that separator, because without it the message is considered truncated and splitting records would be unreliable.","triggerScenarios":"A transport delivers a partial buffer (missing the trailing 0x1e), a custom transport forgets to append the separator, or a proxy truncates the response body. Also if a caller feeds parseMessages an arbitrary string that is not a full framed message.","commonSituations":"Streaming/partial reads not reassembled before parsing, a hand-rolled transport, response-body truncation by a reverse proxy or CDN, or a bug in message assembly that drops the trailing byte.","solutions":["Reassemble transport chunks into complete framed messages before passing to parseMessages (the built-in transports already do this).","Ensure any custom transport appends RecordSeparator (0x1e) on write and keeps reads framed.","Check for a proxy/CDN truncating the body or altering encoding.","Log raw input bytes (logMessageContent:true) to confirm the trailing separator is present."],"exampleFix":"// before (custom transport, separator missing)\nreturn output;\n\n// after\nimport { TextMessageFormat } from './TextMessageFormat';\nreturn TextMessageFormat.write(output); // appends 0x1e","handlingStrategy":"try-catch","validationCode":"function isFullyFramed(input: string): boolean {\n  return input.length > 0 && input[input.length - 1] === String.fromCharCode(0x1e);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const messages = TextMessageFormat.parse(input);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Message is incomplete.') {\n    // buffer more bytes until the record separator arrives\n  }\n}","preventionTips":["Buffer transport reads until a 0x1e terminator is present before parsing.","Custom transports must append the record separator on write.","Watch for proxies that alter response encoding and drop the trailing byte."],"tags":["typescript","signalr","protocol","message-framing","transport"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}