| | |

Serialization 10 📄 MessagePack — Binary JSON Without a Schema

⏱️ Estimated read: 10 min | |

MessagePack is a binary serialization format that preserves JSON’s data model while removing JSON’s textual overhead. It encodes the same types—maps, arrays, strings, numbers, booleans, null—but writes them as compact binary tags and length-prefixed payloads rather than quoted keys and decimal digits. The format is schema-less: the type information is embedded in the first byte of each value, so a decoder can read the data without a separate .proto file or schema registry. This is its defining trade-off against Protobuf: MessagePack is simpler to adopt, but it provides no evolution contract and no schema-enforced compatibility.

This chapter covers MessagePack’s format families, the schema-less trade-off, the performance characteristics measured against JSON, and the security considerations that arise when the format is used for untrusted input. The sections that follow walk through the binary encoding, the comparison with JSON and Protobuf, and the cases where MessagePack is the correct choice.

Key point: MessagePack is a compact, schema-less binary encoding of JSON’s data model; it is smaller and faster than JSON but provides no schema-enforced evolution guarantees, and its typeless deserialization paths have produced security vulnerabilities in library implementations.


a. The format families and binary encoding

MessagePack defines a set of format families, each identified by a leading byte or byte range. The specification is maintained in the msgpack/msgpack repository, and the format is implemented across dozens of languages .

The primitive types use fixed-length encodings. nil is a single byte 0xc0. Booleans are 0xc2 for false and 0xc3 for true. Integers use the smallest representation that fits: positive fixint stores values 0–127 in one byte, negative fixint stores −32 to −1 in one byte, and larger values use uint8 through uint64 or int8 through int64 with explicit type bytes . Floats use IEEE 754 single or double precision with a type byte.

Strings and binary data use length-prefixed formats. A string of up to 31 bytes uses fixstr with the length in the lower five bits of the first byte. Longer strings use str8, str16, or str32 with 1, 2, or 4 length bytes respectively. Binary data has a parallel bin family, separated from strings in the newer specification . Arrays and maps use fixarray/fixmap for small collections and array16/map16/array32/map32 for larger ones.

# A string "hello" in MessagePack
A5 68 65 6C 6C 6F
# 0xA5 = fixstr, length 5 (0xA5 & 0x1F = 5)
# 68 65 6C 6C 6F = "hello"

# An integer 150
CC 96
# 0xCC = uint8, 0x96 = 150

# A small integer 42 (positive fixint)
2A
# 0x2A = 42, encoded in one byte

The compactness comes from these type-specific encodings. A small integer that takes four or more bytes as a JSON string of digits takes one byte in MessagePack. A short string pays one length byte instead of two quotes and escape handling. The savings are largest for numeric-heavy data and smallest for long text where the quotes are a small fraction of the payload.


b. Schema-less trade-offs against JSON and Protobuf

MessagePack’s position is between JSON and Protobuf. Against JSON, it wins on size and speed. A study comparing the two in Go found that MessagePack reduced end-to-end latency for a 5,000-record dataset from 13.269 ms to 3.894 ms and produced a smaller payload (335.87 KB vs 350.53 KB); the advantage grew with dataset size, particularly in decoding and end-to-end latency . An IEEE study found that MessagePack incurred 144 bytes of first-call container allocation and then zero incremental per-message allocation, compared to JSON’s 528–1,104 bytes per-message allocation . A benchmark of JSON-compatible binary specifications found that schema-less formats including MessagePack can produce messages up to 63% smaller than JSON .

Against Protobuf, MessagePack loses the size competition on most data. The same benchmark found that schema-driven formats like Protobuf can produce bit-strings up to 95% smaller than JSON, and a specific Tier 1 document was 23 bytes in Protobuf versus 61 bytes in MessagePack . The exception is cases where MessagePack happens to be more space-efficient than Protobuf for specific data shapes, which the literature notes but does not generalize . The structural reason is that Protobuf omits field names entirely and uses field numbers in tags, while MessagePack encodes map keys as strings in the payload. For repeated records with the same keys, Protobuf’s advantage compounds.

The schema-less property is the reason to choose MessagePack over Protobuf. There is no .proto file to write, no code generation step, no schema registry to maintain. The encoder and decoder agree on the wire format through the specification, and the data types are embedded in the payload. For a system where the producers and consumers are controlled by the same team, or where the data shape is simple and stable, this simplicity is worth the size cost. For a system that needs strict evolution guarantees across independent teams, Protobuf’s schema is the safer choice.


c. Security considerations

MessagePack’s schema-less design has a security consequence: deserialization must decide what types to construct from the payload. Libraries that support “typeless” or “object” deserialization—where the payload can specify arbitrary types—expose an attack surface similar to YAML’s unsafe loaders.

MessagePack-CSharp, the .NET implementation, had multiple CVEs in 2026 related to this. CVE-2026-48517 affected the ThrowIfDeserializingTypeIsDisallowed safety check, which validated only the outer type name and did not recursively inspect array element types or generic type arguments. A blocked type wrapped inside List<T> or object[] passed the outer check and was materialized by the formatter machinery, enabling remote code execution in applications that deserialized untrusted input . CVE-2026-48109 affected the optional LZ4 decompression path, where a crafted payload could trigger out-of-bounds reads and process termination . CVE-2026-48515 affected multi-dimensional array formatters, which allocated arrays based on payload-declared dimensions before validating the element count, enabling memory exhaustion from a small payload . All three were fixed in versions 2.5.301 and 3.1.7.

The Microsoft documentation for SignalR’s MessagePack protocol recommends reviewing CVE-2020-5234 and applying MessagePackSecurity.UntrustedData when deserializing data from untrusted sources . The pattern is the same as YAML’s safe loader: restrict the types that can be constructed, and do not use the typeless path for input the application did not produce. A MessagePack decoder that constructs only known, registered types is safe; one that constructs arbitrary types from the payload is not.


Complete Example Session

# ============================================
# PART 1: THE SAME DATA IN JSON AND MESSAGEPACK
# ============================================
# JSON (48 bytes):
# {"id":42,"name":"widget","in_stock":true,"tags":["a","b"]}

# MessagePack (conceptual bytes):
# 84                    # fixmap, 4 entries
# A2 69 64              # fixstr "id"
# 2A                    # positive fixint 42
# A4 6E 61 6D 65        # fixstr "name"
# A6 77 69 64 67 65 74  # fixstr "widget"
# A8 69 6E 5F 73 74 6F 63 6B  # fixstr "in_stock"
# C3                    # true
# A4 74 61 67 73        # fixstr "tags"
# 92 A1 61 A1 62        # fixarray 2, "a", "b"

# ============================================
# PART 2: THE SIZE AND SPEED COMPARISON
# ============================================
# Go study, 5000 records :
#   JSON:        350.53 KB, 13.269 ms end-to-end
#   MessagePack: 335.87 KB, 3.894 ms end-to-end
#
# IEEE study :
#   JSON:        528-1,104 B per-message allocation
#   MessagePack: 0 B incremental after first call

# ============================================
# PART 3: THE SCHEMA TRADE-OFF
# ============================================
# Protobuf for the same record: ~25 bytes (field numbers, no names)
# MessagePack: map keys are strings in the payload
# For repeated records, Protobuf's advantage grows

# ============================================
# PART 4: THE SECURITY RULE
# ============================================
# Safe:   decode into a known, registered type
# Unsafe: typeless decode of untrusted input
#         (CVE-2026-48517 in MessagePack-CSharp)

The parts covered the binary encoding, the measured size and speed comparison, the schema trade-off against Protobuf, and the security rule for untrusted input.


Quick Reference

Format Families

FamilyExampleNotes
Nil0xc0Single byte
Boolean0xc2/0xc3false/true
Positive fixint0x00–0x7f0–127, one byte
Negative fixint0xe0–0xff−32 to −1, one byte
Stringfixstr / str8 / str16 / str32Length-prefixed UTF-8
Binarybin8 / bin16 / bin32Length-prefixed bytes
Arrayfixarray / array16 / array32Element count prefix
Mapfixmap / map16 / map32Key-value pair count

Comparison

AspectMessagePackJSONProtobuf
SchemaNoneNoneRequired
Size vs JSON~4–63% smallerBaseline~95% smaller
Decode speed vs JSONFasterBaselineFaster
Evolution guaranteeNoneNoneField numbers
DebuggabilityBinaryTextBinary + schema

Security CVEs (MessagePack-CSharp)

CVEIssueFixed
CVE-2026-48517Typeless type check not recursive2.5.301, 3.1.7
CVE-2026-48109LZ4 decompression OOB read2.5.301, 3.1.7
CVE-2026-48515Multi-dim array allocation before validation2.5.301, 3.1.7

Best Practices

✅ Do This:

# Use MessagePack when JSON is too slow or too large
# and no schema contract is needed                           # ✅

# Decode into a known, registered type
# (avoid typeless deserialization of untrusted input)       # ✅

# Apply MessagePackSecurity.UntrustedData in .NET
# when processing external data                              # ✅ 

# Measure against JSON before switching
# (payload size, encode/decode latency)                     # ✅

❌ Don’t Do This:

# Use MessagePack where Protobuf's evolution guarantees matter
# (independent teams, long-lived schemas)                    # ❌

# Typeless decode of untrusted input
# (CVE-2026-48517 pattern)                                   # ❌ 

# Assume MessagePack is always smaller than Protobuf
# (field names in map keys add up)                           # ❌

# Use MessagePack for human-edited configuration
# (binary is unreadable without a parser)                    # ❌

Common Pitfalls

PitfallWhy It HappensFix
RCE from typeless decodePayload specifies arbitrary typesDecode into registered types only
Memory exhaustionArray dimensions trusted before countUse patched library versions
No schema evolutionSchema-less formatCoordinate changes across teams
Debugging difficultyBinary payloadUse a MessagePack decoder
Protobuf is smallerMap keys in payloadAccept or switch to Protobuf

Real-World Examples

1. Small integer

2A  # 42 in one byte

2. String

A5 68 65 6C 6C 6F  # "hello"

3. Map

84 A2 69 64 2A ...  # 4-entry map

4. Array

92 A1 61 A1 62  # ["a", "b"]

5. SignalR MessagePack

AddMessagePackProtocol(); // server

6. Untrusted data setting

options.Security = MessagePackSecurity.UntrustedData;

7. Registered type decode

Deserialize<ProductCreated>(bytes) // concrete type

8. Go performance result

MessagePack: 335.87 KB, 3.894 ms

9. JSON performance result

JSON: 350.53 KB, 13.269 ms

10. Patched version

MessagePack-CSharp >= 2.5.301 / 3.1.7

Visual

MessagePack Encoding

┌─────────────────────────────────────────────────────────┐
│  "id": 42                                               │
│                                                         │
│  A2 69 64   2A                                          │
│  ▲  ▲  ▲    ▲                                           │
│  │  │  │    └── positive fixint 42 (1 byte)             │
│  │  │  └────── "id" (2 bytes)                           │
│  │  └───────── fixstr, length 2                         │
│  └──────────── type byte                                │
│                                                         │
│  JSON: "id": 42  → 9 bytes                              │
│  MessagePack:    → 5 bytes                              │
└─────────────────────────────────────────────────────────┘

Position in the Format Landscape

┌─────────────────────────────────────────────────────────┐
│  SCHEMA-LESS                 SCHEMA-BASED               │
│                                                         │
│  JSON    MessagePack         Protobuf    Avro           │
│    │         │                 │          │            │
│    ▼         ▼                 ▼          ▼            │
│  Text    Binary              Binary     Binary          │
│  Large   Compact             Compact    Compact         │
│  Slow    Fast                Fast       Fast            │
│  No      No                  Field      Schema          │
│  contract contract           numbers    resolution      │
│                                                         │
│  MessagePack: JSON's model, binary encoding, no schema  │
└─────────────────────────────────────────────────────────┘

The Security Rule

┌─────────────────────────────────────────────────────────┐
│  MESSAGEPACK DESERIALIZATION                            │
│                                                         │
│  Payload → Decoder → ?                                  │
│                                                         │
│  Registered type:                                       │
│  Decoder knows the target type, constructs it safely    │
│                                                         │
│  Typeless:                                              │
│  Payload specifies the type, decoder constructs it      │
│  → CVE-2026-48517: blocked type wrapped in List<T>      │
│  → RCE in unpatched MessagePack-CSharp                  │
│                                                         │
│  Rule: do not typeless-decode untrusted input           │
└─────────────────────────────────────────────────────────┘

Summary

ItemValue
TypeSchema-less binary serialization
Data modelJSON-compatible (maps, arrays, scalars)
Size vs JSONUp to 63% smaller
Size vs ProtobufUsually larger
Speed vs JSONFaster (4.1ms vs 13.3ms in one study)
Evolution guaranteeNone
Security riskTypeless deserialization (CVEs)
Safe patternDecode into known, registered types

Key takeaways:

  • MessagePack is JSON’s data model in binary. It encodes the same maps, arrays, strings, numbers, and booleans, but uses type bytes and length prefixes instead of quotes and digits. The result is smaller and faster, with no schema to write or maintain .
  • It sits between JSON and Protobuf. It beats JSON on size and speed, but loses to Protobuf on size because map keys are strings in the payload rather than field numbers in a schema .
  • Schema-less is the reason to choose it and the reason to avoid it. No .proto, no codegen, no registry—but also no evolution contract. For controlled producers and consumers, the simplicity is worth it; for independent teams with long-lived schemas, Protobuf is safer.
  • Typeless deserialization is the security hazard. MessagePack-CSharp’s 2026 CVEs all stemmed from the typeless path constructing types from the payload. Decode into known, registered types, and apply MessagePackSecurity.UntrustedData when the input is external .
  • The performance advantage grows with volume. The Go study found the gap widened with dataset size, particularly in decoding and end-to-end latency . At low volumes, JSON’s debuggability is worth more than MessagePack’s speed.

Remember: MessagePack is the format for when JSON is too slow or too large but a schema is too much ceremony. It keeps JSON’s flexibility and removes its text overhead. The cost is that the payload carries its own type information, which means a decoder that trusts the payload can be made to construct things the application did not intend. Use it with registered types, measure the gain against JSON, and do not expect it to replace Protobuf where evolution guarantees matter.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!