dotnet/aspnetcore · error · RuntimeException
Error writing MessagePack data.
Error message
Error writing MessagePack data.
What it means
writeMessage wraps any MessagePackException or IOException raised while serializing a hub message's bytes into a RuntimeException with this message. It is a generic catch-all for an underlying serialization/packing failure of an InvocationMessage, StreamItem, Completion, etc. The original exception is attached as the cause, so the real reason (e.g. Jackson cannot serialize a type) is in ex.getCause().
Solutions
- Inspect ex.getCause() in the debugger/logs to find the underlying MessagePackException/IOException.
- Replace the offending argument type with one Jackson can serialize to MessagePack (POJO with default constructor, primitives, collections).
- Pin compatible versions of org.msgpack:msgpack-core and org.msgpack:msgpack-jackson-dataformat.
Example fix
// before
protocol.writeMessage(new InvocationMessage(headers, id, target, new Object[] { unsupportedType }));
// after - serialize a POJO Jackson can handle
public class MyArg { public String a; public int b; }
protocol.writeMessage(new InvocationMessage(headers, id, target, new Object[] { new MyArg() })); Defensive patterns
Strategy: try-catch
Validate before calling
// Verify arguments are Jackson-MessagePack-serializable before sending
ObjectMapper mp = new ObjectMapper(new MessagePackFactory());
for (Object arg : args) {
try { mp.writeValueAsBytes(arg); }
catch (Exception e) { throw new IllegalArgumentException("Unserializable argument " + arg, e); }
} Type guard
// POJO with no-arg constructor and Jackson-friendly fields is the safe shape
static boolean isLikelySerializable(Object o) {
return o == null || o instanceof Number || o instanceof CharSequence || o instanceof Boolean
|| o instanceof Collection || o instanceof Map
|| (o.getClass().getDeclaredConstructors().length > 0); // rough
} Try / catch
try {
protocol.writeMessage(message);
} catch (RuntimeException ex) {
Throwable cause = ex.getCause();
// log cause (MessagePackException / IOException) to find the offending field
log.error("writeMessage failed: {}", cause, ex);
throw ex;
} Prevention
- Keep hub method argument types as simple POJOs with a default constructor.
- Pin compatible msgpack-core / msgpack-jackson-dataform versions.
- Always inspect getCause() when this generic message appears.
When it happens
Trigger: Any I/O or msgpack-core failure inside the try at MessagePackHubProtocol.java:147-188: a MessageBufferPacker write error, a Jackson ObjectMapper failure encoding an argument that has no MessagePack serializer, or a null field the packer cannot handle.
Common situations: Passing a non-serializable argument (e.g. a type with no default constructor / Jackson-incompatible), version drift in the msgpack/jackson-databind dependencies, or a corrupted ObjectMapper instance.
Related errors
- Error reading MessagePack data.
- Unexpected message type
- Error reading length header.
- Extension types are not supported
- Invalid invocation result kind.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/e772f0442dafd4ee.
Report an issue: GitHub.
Appendix: source
Thrown at src/SignalR/clients/java/signalr/messagepack/src/main/java/com/microsoft/signalr/messagepack/MessagePackHubProtocol.java:189
default:
throw new RuntimeException(String.format("Unexpected message type: %d", messageType.value));
}
int length = message.length;
List<Byte> header = Utils.getLengthHeader(length);
byte[] messageWithHeader = new byte[header.size() + length];
int headerSize = header.size();
// Write the length header, then all of the bytes of the original message
for (int i = 0; i < headerSize; i++) {
messageWithHeader[i] = header.get(i);
}
for (int i = 0; i < length; i++) {
messageWithHeader[i + headerSize] = message[i];
}
return ByteBuffer.wrap(messageWithHeader);
} catch (MessagePackException | IOException ex) {
throw new RuntimeException("Error writing MessagePack data.", ex);
}
}
private HubMessage createInvocationMessage(MessageUnpacker unpacker, InvocationBinder binder, int itemCount, ByteBuffer payload) throws IOException {
Map<String, String> headers = readHeaders(unpacker);
// invocationId may be nil
String invocationId = null;
if (!unpacker.tryUnpackNil()) {
invocationId = unpacker.unpackString();
}
// For MsgPack, we represent an empty invocation ID as an empty string,
// so we need to normalize that to "null", which is what indicates a non-blocking invocation.
if (invocationId == null || invocationId.isEmpty()) {
invocationId = null;
}
View on GitHub (pinned to 3600ca084e)