dotnet/aspnetcore · error · Error
Message is incomplete.
Error message
Message is incomplete.
What it means
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.
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.
Example fix
// before (test/fixture)
const msgs = TextMessageFormat.parse('{"type":6}'); // throws
// after
const rs = String.fromCharCode(0x1e);
const msgs = TextMessageFormat.parse('{"type":6}' + rs); Defensive patterns
Strategy: validation
Validate before calling
function isCompleteTextFrame(input: string): boolean {
return input.length > 0 && input[input.length - 1] === String.fromCharCode(0x1e);
}
// before calling TextMessageFormat.parse:
if (!isCompleteTextFrame(raw)) {
// buffer more bytes or surface a framing error; do not call parse
} Type guard
function isCompleteTextFrame(input: unknown): input is string {
return typeof input === 'string'
&& input.length > 0
&& input.charCodeAt(input.length - 1) === 0x1e;
} Try / catch
try {
const messages = TextMessageFormat.parse(buffer);
} catch (e) {
if (e instanceof Error && e.message === 'Message is incomplete.') {
// keep buffering; this chunk was partial
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Message is incomplete.
- MessagePack message was length
- Unknown value: .
- Cannot read message size.
- could not find global
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/96d2aa4cd5cb8681.
Report an issue: GitHub.
Appendix: source
Thrown at src/SignalR/clients/ts/signalr/src/TextMessageFormat.ts:16
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// Not exported from index
/** @private */
export class TextMessageFormat {
public static RecordSeparatorCode = 0x1e;
public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);
public static write(output: string): string {
return `${output}${TextMessageFormat.RecordSeparator}`;
}
public static parse(input: string): string[] {
if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) {
throw new Error("Message is incomplete.");
}
const messages = input.split(TextMessageFormat.RecordSeparator);
messages.pop();
return messages;
}
}
View on GitHub (pinned to 3600ca084e)