dotnet/aspnetcore · error · RuntimeException
Message is incomplete.
Error message
Message is incomplete.
What it means
GsonHubProtocol splits the ByteBuffer on the Record Separator (\u001e). If the payload's last character is not that separator, the framing is treated as truncated and the parse is rejected before attempting JSON parsing, mirroring the TypeScript TextMessageFormat behavior.
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 294cab2f9b)
Solutions
- Buffer the full framed payload (including the trailing 0x1e) before calling parseMessages.
- Ensure custom transports append RECORD_SEPARATOR on write and preserve framing on read.
- Check for proxies/CDNs truncating the response body.
- Log the raw payload bytes to confirm the trailing separator.
Example fix
// before
String payload = "{...json...}"; // missing separator
// after
String payload = "{...json...}" + "\u001e"; Defensive patterns
Strategy: try-catch
Validate before calling
boolean isFullyFramed(String payload) {
return payload != null && payload.length() > 0
&& payload.substring(payload.length() - 1).equals("\u001e");
} Try / catch
try {
protocol.parseMessages(buffer, binder);
} catch (RuntimeException e) {
if ("Message is incomplete.".equals(e.getMessage())) {
// re-buffer until the record separator is present
}
} Prevention
- Fully buffer framed records (trailing 0x1e) before parsing.
- Custom transports must append the record separator on write.
- Validate payloads in tests with the separator included.
When it happens
Trigger: A truncated/partial ByteBuffer, a custom transport that does not append the separator, or message reassembly that drops the trailing byte before reaching the parser.
Common situations: Partial network reads not fully buffered, a buggy custom transport, proxy body truncation, or test fixtures that omit the separator.
Related errors
- Message is incomplete.
- Invalid payload.
- Expected either 'error' or 'result' to be provided, but not
- The message type %s is not supported yet.
- Error reading JSON.
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/0e7682cf5932e538.
Report an issue: GitHub.