Skip to content

Protocol Versions

Kronotop speaks two protocol versions on the same port. A new connection starts in RESP2, and HELLO switches it for the rest of the connection.

RESP3 is the better choice for new applications, and every example in this documentation uses RESP3 output. This page is for clients that stay on RESP2.

RESP3 has types that RESP2 does not have. When a reply holds one of them, the server rewrites it before sending, so a RESP2 client never receives a type it cannot parse. A reply that holds no such type is sent as it is.

RESP3 typeWhat a RESP2 client receives
mapflat array of keys and values, field order is kept
setarray
booleaninteger 1 for true, 0 for false
doublebulk string that holds 8 raw bytes, see below
nullbulk string with a length of -1
big numberbulk string
verbatim stringbulk string without the format prefix

The rewrite covers the whole reply, not only the outer level. A map nested three levels deep is flattened the same way.

TASK.ADMIN LIST shows the difference. On a RESP3 connection:

127.0.0.1:3320> TASK.ADMIN LIST
1# journal:cleanup-task =>
1# running => (false)
2# finished => (false)
3# started_at => (integer) 1752582119
4# last_run => (integer) 0

The same reply on a RESP2 connection:

127.0.0.1:3320> TASK.ADMIN LIST
1) "journal:cleanup-task"
2) 1) "running"
2) (integer) 0
3) "finished"
4) (integer) 0
5) "started_at"
6) (integer) 1752582119
7) "last_run"
8) (integer) 0

This is the only place where a RESP2 client can read a value wrongly and get no error.

RESP2 has no double type. The value is sent as a bulk string that holds the 8 byte IEEE 754 big-endian form of the number. It is not the number written as text, so printing it gives you a few unreadable characters. Read the 8 bytes as a big-endian double instead.

Doubles appear in the BUCKET.VECTOR score and in the garbage_percentage and fill_ratio fields of VOLUME.STATS.

For most commands the RESP2 reply is the RESP3 reply with the types swapped. A few commands use a different shape instead. They drop the field names, so the order of the values tells you what they are.

CommandRESP2 reply
BUCKET.QUERY, BUCKET.ADVANCE[cursor_id, [documents]]
BUCKET.DELETE, BUCKET.UPDATE[cursor_id, [object_ids]]
BUCKET.VECTOR[[score, document], ...]

Each of those command pages shows both versions side by side.

Use RESP3 unless your client library does not support it. In a map every value comes with its field name. If a later release adds a field, the fields you already read stay where they are. On RESP2 the same reply is a flat array, so your parser depends on the order.