dotnet/aspnetcore · error · Error

Message is incomplete.

Error message

Message is incomplete.

What it means

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.

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 294cab2f9b)

Solutions

  1. Reassemble transport chunks into complete framed messages before passing to parseMessages (the built-in transports already do this).
  2. Ensure any custom transport appends RecordSeparator (0x1e) on write and keeps reads framed.
  3. Check for a proxy/CDN truncating the body or altering encoding.
  4. Log raw input bytes (logMessageContent:true) to confirm the trailing separator is present.

Example fix

// before (custom transport, separator missing)
return output;

// after
import { TextMessageFormat } from './TextMessageFormat';
return TextMessageFormat.write(output); // appends 0x1e
Defensive patterns

Strategy: try-catch

Validate before calling

function isFullyFramed(input: string): boolean {
  return input.length > 0 && input[input.length - 1] === String.fromCharCode(0x1e);
}

Try / catch

try {
  const messages = TextMessageFormat.parse(input);
} catch (e) {
  if (e instanceof Error && e.message === 'Message is incomplete.') {
    // buffer more bytes until the record separator arrives
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/96d2aa4cd5cb8681. Report an issue: GitHub.