dotnet/aspnetcore · error · RuntimeException
Message is incomplete.
Error message
Message is incomplete.
What it means
Thrown by GsonHubProtocol.parseMessages when the UTF-8 payload string does not end with the RECORD_SEPARATOR character (U+001E). Mirror of the TS TextMessageFormat guard (error 160): the Java JSON protocol splits frames on 0x1E and rejects an unterminated frame rather than silently dropping it. Empty payloads return null earlier, so this throw specifically means non-empty but unframed input.
Solutions
- Use the built-in WebSocketTransport/LongPollingTransport which buffer complete frames before handing them to the protocol.
- If parsing manually in tests, always append RECORD_SEPARATOR: String payload = json + Character.toString((char)0x1e).
- Inspect the server response: a missing 0x1E on the wire usually means you connected to a non-SignalR endpoint or a proxy stripped the byte.
- Verify the server is an ASP.NET Core SignalR endpoint at the negotiated version.
Example fix
// before (test)
List<HubMessage> msgs = protocol.parseMessages(ByteBuffer.wrap("{\"type\":6}".getBytes()), null); // throws
// after
String frame = "{\"type\":6}" + (char)0x1e;
List<HubMessage> msgs = protocol.parseMessages(ByteBuffer.wrap(frame.getBytes(StandardCharsets.UTF_8)), null); Defensive patterns
Strategy: validation
Validate before calling
boolean isCompleteFrame(ByteBuffer buf) {
if (buf.remaining() == 0) return true; // empty is handled separately
byte last = buf.get(buf.limit() - 1);
return last == (byte) 0x1e;
}
if (!isCompleteFrame(payload)) {
// buffer more; do not call parseMessages Type guard
boolean endsWithRecordSeparator(String s) {
return !s.isEmpty() && s.charAt(s.length() - 1) == 0x1e;
} Try / catch
try {
protocol.parseMessages(payload, null);
} catch (RuntimeException e) {
if ("Message is incomplete.".equals(e.getMessage())) {
// re-buffer the partial frame and wait for more bytes
} else throw e;
} Prevention
- Use built-in transports; they buffer whole frames before invoking the protocol.
- In tests, append (char)0x1e to every payload.
- Verify the server is a real SignalR endpoint and no proxy strips control bytes.
When it happens
Trigger: A ByteBuffer passed to parseMessages whose remaining bytes do not terminate with 0x1E; a custom transport that delivers a sliced frame; a server or proxy that strips the record separator; malformed test payloads.
Common situations: Replacing the default transport with one that does not buffer until a full frame; an intermediary (load balancer/proxy) truncating control characters; version mismatch where a non-SignalR endpoint answered the connection; tests feeding raw JSON without the separator.
Related errors
- Message is incomplete.
- MessagePack message was length
- Error reading JSON.
- Error reading length header.
- Error reading MessagePack data.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/0e7682cf5932e538.
Report an issue: GitHub.
Appendix: source
Thrown at src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/GsonHubProtocol.java:60
@Override
public List<HubMessage> parseMessages(ByteBuffer payload, InvocationBinder binder) {
String payloadStr;
// If the payload is readOnly, we have to copy the bytes from its array to make the payload string
if (payload.isReadOnly()) {
byte[] payloadBytes = new byte[payload.remaining()];
payload.get(payloadBytes, 0, payloadBytes.length);
payloadStr = new String(payloadBytes, StandardCharsets.UTF_8);
// Otherwise we can allocate directly from its array
} else {
// The position of the ByteBuffer may have been incremented - make sure we only grab the remaining bytes
payloadStr = new String(payload.array(), payload.position(), payload.remaining(), StandardCharsets.UTF_8);
}
if (payloadStr.length() == 0) {
return null;
}
if (!(payloadStr.substring(payloadStr.length() - 1).equals(RECORD_SEPARATOR))) {
throw new RuntimeException("Message is incomplete.");
}
String[] messages = payloadStr.split(RECORD_SEPARATOR);
List<HubMessage> hubMessages = new ArrayList<>();
try {
for (String str : messages) {
HubMessageType messageType = null;
String invocationId = null;
String target = null;
String error = null;
ArrayList<Object> arguments = null;
JsonArray argumentsToken = null;
Object result = null;
Exception argumentBindingException = null;
JsonElement resultToken = null;
JsonReader reader = new JsonReader(new StringReader(str));
reader.beginObject();
View on GitHub (pinned to 3600ca084e)