JusDB
Free DB Audit
NoSQL Databases

Valkey 8 to 9.2: The Changes That Break Client Libraries

Find the Valkey 8.0 to 9.1 reply, error and protocol changes that break client libraries, the client versions that handle them, and a test to run before you upgrade.

JusDB Team
Published November 5, 2026
Updated November 10, 2026
19 min read

The Valkey clients page says that anything speaking the Redis 7.2 wire protocol works with Valkey 7.2 and later. For ordinary commands that holds. The exceptions sit where applications rarely test: the connection handshake, error text, and cluster topology replies. Against Valkey 9.1, go-redis 9.20.0 and earlier fail on CLUSTER SHARDS with redis: unexpected key "id" in CLUSTER SHARDS reply. Since 8.0, HELLO answers server: valkey, INFO prints server_mode instead of redis_mode, and the -LOADING, -BUSY and -MISCONF errors name Valkey. Patch releases on every branch have also changed replies on the wire. This post lists the changes from 8.0 to 9.1, labels what the 9.2 release candidate adds, names the client versions that handle them, and gives a test harness to run before you upgrade.

TL;DR
  • go-redis before v9.20.1 cannot parse CLUSTER SHARDS from Valkey 9.1, which always adds a shard-level id. Cluster routing uses CLUSTER SLOTS and keeps working; direct ClusterShards() calls fail.
  • Since 8.0, HELLO reports valkey and the Valkey version, INFO prints server_mode, and five LOADING, BUSY and MISCONF errors say Valkey. redis_version stays 7.2.4.
  • extended-redis-compatibility yes restores the Redis identity and those five errors, but not the new topology fields, and valkey.conf says it will be removed.
  • Patch releases change replies too: strict CRLF parsing, COMMAND INFO RESP3 types, LSET range errors, and oversized cluster PUBLISH rejection.
  • Replay the commands your application really sends against the old and new versions, and diff the replies, before the server changes.

Scope and review date: November 2026. This post covers Valkey 8.0 through 9.1.3, the current GA release (26 October 2026), and labels behaviour from the 9.2 release candidate (9.2.0-rc2, 4 November 2026). Release notes, Valkey server source at the 9.0.7, 9.1.3 and 9.2.0-rc2 tags, and client library source were read on 10 November 2026. The commands were checked against that documentation and source, not run in a lab, so run the harness at the end on staging.


Why do client libraries break when Valkey says it is compatible?

Because compatibility is promised for the protocol, and the breaks happen in parsers and string matches built on top of it. The Valkey clients page says "most existing clients need no changes to migrate". The project's own issue #4891, opened 14 October 2026, says there is no shared contract for client behaviour, and that the client-changes-needed label already holds 74 issues and pull requests.

Three kinds of client code break. A strict parser rejects a map key it does not know. An error handler matches the full message text instead of the error code. A feature gate reads the server name or version from HELLO or INFO and takes a different path.

Where a strict CLUSTER SHARDS parser fails on Valkey 9.1

Follow the numbers: the server reply is valid, the client parser rejects it

Strict client parser failing on a new CLUSTER SHARDS field Step 1: the application calls ClusterShards in go-redis 9.20.0. Step 2: go-redis sends CLUSTER SHARDS to a Valkey 9.1 node. Step 3: Valkey replies with a shard map that has three keys, slots, nodes and id, and the strict parser returns an unexpected key error to the application. Step 4: upgrading to go-redis 9.20.1, which skips unknown keys, removes the error. Application calls ClusterShards() go-redis 9.20.0 strict map parser Valkey 9.1 shard map: 3 keys Parse error unexpected key "id" go-redis 9.20.1 skips unknown keys 1 2 CLUSTERSHARDS 3 slots, nodes, id 4 upgrade

Routing is not the failure point: the go-redis cluster client builds its slot map from CLUSTER SLOTS. Code that calls ClusterShards() itself, such as operators and admin tools, is what fails.

What do HELLO and INFO report on Valkey 8.0 and 9.1?

Valkey 7.2.x still answered HELLO with server: redis. From 8.0, the server source returns server: valkey and the real Valkey version, such as 9.1.3. INFO server renames redis_mode to server_mode, and keeps redis_version:7.2.4 on every release. The 9.1.3 version.h calls it the Redis OSS compatibility version that "should never exceed 7.2.x".

Other identity fields arrived in steps. server_name, valkey_version and an availability_zone line (empty by default) are in INFO server from 8.0. valkey_release_stage appears in 8.1. HELLO adds availability_zone in 8.1, and only when the availability-zone config is set.

HELLO 3 and INFO server on a Valkey 9.1.3 node

Illustrative output, shaped from the 9.1.3 source: the id and module list vary

ops@app-01:~
$ valkey-cli -3 HELLO 31# "server" => "valkey"2# "version" => "9.1.3"3# "proto" => (integer) 34# "id" => (integer) 75# "mode" => "standalone"6# "role" => "master"7# "modules" => ...$ valkey-cli INFO server | grep -E \ '^(redis_version|server_name|valkey_version|server_mode):'redis_version:7.2.4server_name:valkeyvalkey_version:9.1.3server_mode:standalone

Trust valkey_version: redis_version is a frozen compatibility value. The HELLO reference page still shows "server" => "redis" in its examples; the server source does not.

bash
# Record what each node reports before and after the upgrade
for h in vk-1 vk-2 vk-3; do
  echo "== $h"
  valkey-cli -h "$h" INFO server \
    | grep -E '^(redis_version|server_name|valkey_version|server_mode|redis_mode):'
done

Interpretation: on 8.0 and later you should see server_mode, not redis_mode. A dashboard or health check that reads redis_mode gets nothing. Any script that gates on redis_version treats every Valkey release as 7.2.4.

One more 9.1 change shows up here. 9.1 moved Lua into a module that the server loads at startup. Reading the 9.1.3 source, it is registered like any other module, so expect MODULE LIST and the modules field of HELLO to list lua. Check this on staging if a tool treats a non-empty module list as "Redis Stack".

What does extended-redis-compatibility change, and what does it not?

The extended-redis-compatibility config, added in 8.0 and off by default, makes Valkey present itself as Redis. In the 9.1.3 source it switches three things: the HELLO server name and version, the INFO mode field, and five shared error strings (LOADING, three BUSY variants, and MISCONF). It does not touch the Lua error texts, transaction errors, or the new CLUSTER SHARDS and CLUSTER SLOTS fields.

The same commands on Valkey 8.0, 9.1, and 9.1 in compatibility mode

Switch panels to see which replies a client parser will meet

Valkey 8.0

ReplyValkey 8.0
HELLO servervalkey
HELLO version8.0.x
HELLO availability_zoneNever sent (added in 8.1)
INFO mode fieldserver_mode
INFO redis_version7.2.4
-LOADING textValkey is loading the dataset in memory
CLUSTER SHARDS keys per shardslots, nodes
Node availability-zone in CLUSTER SHARDS and SLOTSNever sent
CLIENT REPLY inside MULTIAccepted

Valkey 9.1

ReplyValkey 9.1
HELLO servervalkey
HELLO version9.1.3
HELLO availability_zoneSent when availability-zone is set
INFO mode fieldserver_mode
INFO redis_version7.2.4
-LOADING textValkey is loading the dataset in memory
CLUSTER SHARDS keys per shardslots, nodes, id
Node availability-zone in CLUSTER SHARDS and SLOTSSent when availability-zone is set
CLIENT REPLY inside MULTIRejected; EXEC then fails with EXECABORT (since 9.0)

9.1, compatibility on

Reply9.1 with extended-redis-compatibility yes
HELLO serverredis
HELLO version7.2.4
HELLO availability_zoneSent when availability-zone is set
INFO mode fieldredis_mode
INFO redis_version7.2.4
-LOADING textRedis is loading the dataset in memory
CLUSTER SHARDS keys per shardslots, nodes, id
Node availability-zone in CLUSTER SHARDS and SLOTSSent when availability-zone is set
CLIENT REPLY inside MULTIRejected; EXEC then fails with EXECABORT (since 9.0)

Not a full rollback: compatibility mode fixes name and version checks only. A strict topology parser still breaks on 9.1, with the setting on or off.

bash
# Runtime switch; affects HELLO and error replies from the next command on
valkey-cli CONFIG SET extended-redis-compatibility yes
valkey-cli CONFIG GET extended-redis-compatibility
# Persist it only if you need it across restarts
valkey-cli CONFIG REWRITE

Interpretation: the config is MODIFIABLE_CONFIG in the 9.1.3 source, so no restart is needed. It also changes client behaviour you may not expect. valkey-go v1.0.79 reads the major version from HELLO and uses CLUSTER SHARDS for topology only when it is 8 or higher, so with compatibility on, it falls back to CLUSTER SLOTS.

A temporary setting

The 9.1.3 valkey.conf calls it "a temporary configuration added in Valkey 8.0" that "is scheduled to have no effect and will be removed in a future version". It still exists in 9.2.0-rc2. Use it to buy time while you fix clients, not as the fix.

Why does go-redis fail on CLUSTER SHARDS against Valkey 9.1?

Valkey 9.1.0 added two fields to CLUSTER SHARDS: a shard-level id, which is always present, and a node-level availability-zone, sent only when the node has one configured. The CLUSTER SHARDS reference lists both under 9.1.0. In the source, 9.0.7 writes each shard as a 2-key map and 9.1.3 as a 3-key map. CLUSTER SLOTS also gained availability-zone inside its networking metadata map.

go-redis 9.20.0 and earlier parse the CLUSTER SHARDS reply with a switch that returns an error on any unknown key. Because the shard id is always there, ClusterShards() against any 9.1 cluster returns redis: unexpected key "id" in CLUSTER SHARDS reply, whether or not you set an availability zone. Valkey issue #3909 (opened 3 June 2026) is still open with the label major-decision-deferred. The maintainers chose to keep the fields and wait to see whether more users report problems. The fix went into the client: go-redis v9.20.1 (11 June 2026) skips unknown shard and node keys.

bash
# Shape of the reply on a cluster node (jq prints keys sorted)
valkey-cli -3 --json CLUSTER SHARDS | jq '.[0] | keys'
# 9.0.x: ["nodes", "slots"]
# 9.1.x: ["id", "nodes", "slots"]

Interpretation: if the third key is there, any client or tool that rejects unknown keys will fail on this node. Run it on one upgraded staging node before you touch production.

bash
# Which go-redis version does each service build with, and who calls ClusterShards?
go list -m github.com/redis/go-redis/v9
grep -rn --include='*.go' 'ClusterShards(' .

Interpretation: a version below v9.20.1 plus any ClusterShards( hit is a confirmed break on 9.1. With no hits, the cluster client still routes correctly, because loadState in go-redis v9.20.0 builds the slot map from CLUSTER SLOTS. Upgrade anyway, so a later dependency that calls it does not surprise you.

Which error replies changed between 8.0 and 9.1?

The error codes did not change; the text after them did. Valkey 8.0 removed "Redis" from error replies (8.0.0 release notes, #206) and made nested transactions abort (#723). 9.0 added more transaction and auth ordering changes (9.0.0-rc1 notes).

SinceBeforeAfter
8.0LOADING Redis is loading the dataset in memoryLOADING Valkey is loading the dataset in memory
8.0BUSY Redis is busy running a script... (and the FUNCTION and module variants)BUSY Valkey is busy running a script...
8.0MISCONF Redis is configured to save RDB snapshots...MISCONF Valkey is configured to save RDB snapshots...
8.0Lua: Unknown Redis command called from scriptUnknown command called from script
8.0Lua: Wrong number of args calling Redis command from scriptWrong number of args calling command from script
8.0MULTI inside MULTI: ERR MULTI calls can not be nested, transaction stays openERR Command 'multi' not allowed inside a transaction, and EXEC fails with EXECABORT
8.0WATCH inside MULTI: ERR WATCH inside MULTI is not allowedERR Command 'watch' not allowed inside a transaction, transaction aborted
9.0CLIENT REPLY ON|OFF|SKIP inside MULTI acceptedRejected, transaction aborted
9.0Unknown command before AUTH: ERR unknown commandNOAUTH first (#1475)

One change came and went. 8.0.0 and 8.0.1 answered UNSUBSCRIBE from a client that was not subscribed with NOSUB 'unsubscribe' command executed not in subscribed mode. The 8.0.2 notes revert it as "an unintended breaking change". If you still run 8.0.0 or 8.0.1, move to the latest 8.0 patch.

bash
# Find code and tests that match the old message text
grep -rnE "Redis is (loading|busy|configured)|Unknown Redis command|calling Redis command|MULTI calls can not be nested|WATCH inside MULTI|redis_mode" \
  ./src ./tests

Interpretation: every hit is a handler or assertion that stops matching on 8.0 and later. Retry logic keyed on the "Redis is loading" text is the risky one: it silently stops retrying while a node loads its dataset.

Match on the error code

Key handlers on the first word (LOADING, BUSY, EXECABORT, NOAUTH). redis-py v8.1.0, for example, maps errors on the first word, so LOADING still raises BusyLoadingError on Valkey. Its ERR mappings still compare full message text.

bash
# The nested transaction change, on a 9.1 staging node
valkey-cli -3 <<'EOF'
MULTI
SET k 1
MULTI
EXEC
EOF
# 8.0 and later: the second MULTI returns
#   ERR Command 'multi' not allowed inside a transaction
# and EXEC returns EXECABORT, so SET k 1 is not applied

Interpretation: on Redis 7.2 and Valkey 7.2, the same sequence ran SET k 1. Client wrappers that open a transaction without tracking whether one is already open will now lose writes, so check them.

Which patch releases change replies on the wire?

Valkey backports fixes to every maintained branch at once, and several of them change what a client receives. Each row below is from the release notes; "first fixed in" is per branch. The most recent wave (7.2.15, 8.0.12, 8.1.11, 9.0.7 and 9.1.3) shipped on 26 and 27 October 2026.

ChangeFirst fixed inWhat a client sees
Strict CRLF parsing (#2872)7.2.14, 8.0.10, 8.1.8, 9.0.5, 9.1.0Requests ending in a bare LF get a protocol error instead of being misparsed
COMMAND INFO subcommands in RESP3 (#3939)7.2.14, 8.0.10, 8.1.9, 9.0.5, 9.1.1An empty Array instead of a Set for commands without subcommands
HPERSIST on a wrong-type key (#3516)9.0.5, 9.1.0A valid error instead of a malformed reply that desynchronized the connection
Corrupted replies with I/O threads (#4060)9.0.5, 9.1.1No more dropped leading bytes in replies
Torn RESP3 push frames on self-publish (#4253)9.0.6, 9.1.2Push frames arrive whole when a client publishes to a channel it subscribes to
BLPOP followed by a partial pipelined command (#4531)9.0.6, 9.1.2No permanent client hang
LSET out-of-range index on 64-bit builds (#4823)7.2.15, 8.0.12, 8.1.11, 9.0.7, 9.1.3An index out of range error instead of a silent write to the wrong element
Oversized PUBLISH and SPUBLISH in cluster mode (#5030)7.2.15, 8.0.12, 8.1.11, 9.0.7, 9.1.3An error reply (a security fix, GHSA-f4q9-j22m-xqq9)
bash
# See the RESP3 type of a reply: sets are numbered with "~", arrays with ")"
valkey-cli -3 --no-raw COMMAND INFO GET

Interpretation: in the subcommands field of GET, an older patch prints an empty set and a fixed one prints an empty array. --json renders both as [], so use --no-raw when the RESP3 type matters to your parser.

What does the Valkey 9.2 release candidate change for clients?

These are 9.2 release candidate behaviours (9.2.0-rc1 on 16 September 2026, rc2 on 4 November 2026), not GA. Plan tests for them, but expect changes before 9.2.0 ships. From the 9.2.0-rc2 and rc1 notes:

  • Large sorted sets use a B+ tree, so OBJECT ENCODING returns btree instead of skiplist (#4359).
  • ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZCOUNT and ZRANGESTORE reject a bare ( or an empty string as a score bound instead of treating it as 0 (#4054).
  • Inline commands containing a raw NUL byte get a protocol error (#4759).
  • Cluster replicas redirect keyless commands such as SCAN and FLUSHDB to the primary for clients that sent CLIENT CAPA REDIRECT and not READONLY (#3505). Issue #4891 calls this a breaking change.
  • SISMEMBER gains an XX option that returns -1 when the key does not exist (#2972).
bash
# Code that asserts on encodings or builds score bounds from user input
grep -rnE "OBJECT ENCODING|skiplist|ZRANGEBYSCORE|ZCOUNT" ./src ./tests
valkey-cli COMMAND DOCS SISMEMBER

Interpretation: tests that expect skiplist fail on 9.2. Also note a metadata error: the 9.2.0-rc2 sismember.json, which feeds COMMAND DOCS, dates XX to 9.1.0, but 9.1.3 has a fixed arity of 3 and no XX. Feature-detect with a probe on the real server, not with the since history.

Which client versions handle these changes?

We read the source or release notes of these clients on 10 November 2026. This is not a full survey. Issue #4891 notes that each library picks its own topology command and retry rules, so check yours the same way.

Client or toolVersion readWhat it does
go-redisv9.20.0, v9.20.1 (latest v9.23.0)v9.20.0 and earlier fail on unknown CLUSTER SHARDS keys; v9.20.1 skips them. Cluster routing uses CLUSTER SLOTS
valkey-gov1.0.79Uses CLUSTER SHARDS for topology when the HELLO major version is 8 or more; reads keys by name and ignores extras
redis-pyv8.1.0Maps errors by the first word, so the 8.0 text changes after LOADING or BUSY do not change the exception class
Valkey GLIDEREADME on main (latest release v2.5.3)The supported engine table lists Valkey 7.2 through 9.1
redis_exporterv1.97.0Reads server_mode when redis_mode is missing and adds a valkey_version label; its redis_version label stays 7.2.4
bash
# Which libraries and versions are connected right now?
valkey-cli CLIENT LIST \
  | grep -oE 'lib-name=[^ ]* lib-ver=[^ ]*' | sort | uniq -c | sort -rn

Interpretation: this counts connections by the library name and version that clients sent with CLIENT SETINFO. An empty lib-name means the client did not send it, so check that service by hand. The server field is lib-ver; the CLIENT LIST reference calls it lib-version, so a parser written from the docs misses it.

bash
# Versions each service ships, from its own repository
go list -m all | grep -E 'go-redis|valkey-go|valkey-glide'
pip show redis valkey valkey-glide 2>/dev/null | grep -E '^(Name|Version)'
npm ls ioredis iovalkey redis @valkey/valkey-glide

Interpretation: include batch jobs, operators and admin tools. The go-redis break on 9.1 is most likely in tooling, not in the main application.

How do you test client compatibility before upgrading?

Replay the commands your application sends against the current and target versions, diff the replies, then run the client's own tests against the target. Build the command list from what the application really sends: capture it with MONITOR on staging (never on a busy primary), or from integration test traffic.

Client compatibility test before a Valkey upgrade

Eight steps on staging; nothing here touches production

  1. Inventory connected clients

    Count connections by library and version from production, read-only.

    valkey-cli CLIENT LIST | grep -oE \ 'lib-name=[^ ]* lib-ver=[^ ]*' | sort | uniq -c
  2. Capture a command corpus

    Run MONITOR on staging during an integration test, then strip it to one command per line.

    timeout 60 valkey-cli -h staging MONITOR > monitor.log
  3. Start the current and target versions

    Use cluster mode so topology replies are included. Repeat with 9.1.3 as vk-new.

    docker run -d --name vk-old valkey/valkey:9.0.7 \ valkey-server --cluster-enabled yes
  4. Make each a one-node cluster

    Assign all slots so CLUSTER SHARDS and CLUSTER SLOTS return data.

    docker exec vk-old valkey-cli \ CLUSTER ADDSLOTSRANGE 0 16383
  5. Replay the corpus and diff

    Run the script below. Ignore lines that always differ, such as ids and timestamps.

    ./replay.sh corpus.txt vk-old vk-new
  6. Set an availability zone and rerun

    This exercises the optional HELLO and topology fields.

    docker exec vk-new valkey-cli \ CONFIG SET availability-zone zone-a
  7. Run each service's tests against vk-new

    Pin the client versions found in step 1, and include error-path tests.

  8. Upgrade clients, then servers

    Ship client fixes first; they also work against the old server.

Main risk: a reply your corpus never requests. Build it from captured traffic, not from the commands you expect the application to send.

bash
#!/usr/bin/env bash
# replay.sh CORPUS OLD_CONTAINER NEW_CONTAINER
# CORPUS: one command per line, no quoted spaces, throwaway data only
set -euo pipefail
corpus=$1; shift
for c in "$@"; do
  : > "replies-$c.txt"
  while IFS= read -r cmd; do
    echo "### $cmd" >> "replies-$c.txt"
    # word splitting of $cmd is intended
    docker exec "$c" valkey-cli -3 --no-raw $cmd \
      >> "replies-$c.txt" 2>&1 < /dev/null || true
  done < "$corpus"
done
diff -u "replies-$1.txt" "replies-$2.txt" | grep -vE '"id"|run_id|server_time|uptime' || true

Interpretation: every remaining hunk is a reply that differs between versions; check each against the parsers of the clients from step 1. Start the corpus with HELLO 3, INFO server, CLUSTER SHARDS, CLUSTER SLOTS and COMMAND INFO GET, then add what MONITOR captured.

Frequently asked questions

Does Valkey 9.1 break go-redis?

Only for code that calls ClusterShards(). Valkey 9.1 adds a shard-level id to every CLUSTER SHARDS reply, and go-redis 9.20.0 and earlier return "unexpected key" errors on it. The go-redis cluster client routes with CLUSTER SLOTS, so normal reads and writes keep working. go-redis v9.20.1, released 11 June 2026, skips unknown keys, so upgrade before moving to 9.1.

Why does INFO show redis_version 7.2.4 on Valkey 9.1?

It is a fixed compatibility value. Valkey's source defines the Redis OSS compatibility version as 7.2.4 and says it should never exceed 7.2.x, so every Valkey release reports it. Read valkey_version for the real version, and server_name to tell Valkey from Redis. HELLO reports the Valkey version unless extended-redis-compatibility is on.

Should I turn on extended-redis-compatibility?

Only as a short-term workaround. It makes HELLO report redis and 7.2.4, restores redis_mode in INFO, and puts "Redis" back in five LOADING, BUSY and MISCONF errors. It does not hide the new CLUSTER SHARDS fields. Valkey's configuration file calls it temporary and says it will be removed, so fix the clients instead.

Can a Valkey patch release change client behaviour?

Yes. Patch releases on every branch have changed replies on the wire: strict CRLF parsing, the RESP3 type of a COMMAND INFO field, an LSET out-of-range error, and an error for oversized PUBLISH in cluster mode. Most are bug or security fixes, but a client that relied on the old behaviour changes too. Diff the replies on staging before patching.

Key takeaways

  • Move go-redis to v9.20.1 or later before Valkey 9.1, and find every direct ClusterShards() call in tools and operators.
  • Read valkey_version and server_mode; redis_version is always 7.2.4 and redis_mode is gone from 8.0.
  • Match errors on the code, not the text: 8.0 changed the LOADING, BUSY, MISCONF, Lua and nested transaction messages.
  • Treat patch releases and the 9.2 release candidate as client-visible changes, and read their notes before you roll them out.
  • Replay captured traffic against the current and target versions, diff the replies, and upgrade clients before servers.

Check your Valkey clients with JusDB

JusDB's Valkey support team can review your client libraries and tooling against the 8.0 to 9.1 changes, help build and run a replay test on staging, and plan the client and server upgrade order for your Sentinel or cluster setup.

Bring your current Valkey versions, the client libraries each service uses, and any operators or admin tools that talk to the cluster. Your team approves every production change.

Share this article

Database engineering notes

Articles like this one, in your inbox. No spam, unsubscribe anytime.

JusDB Team

Official JusDB content team

Keep reading

Moving a Multi-Database Valkey Instance into a 9.x Cluster

Move a Valkey instance that uses SELECT 1 to 15 into a 9.x cluster: keep or remap databases, copy each one, verify counts per database, and keep a way back.

Redis18 minNov 10, 2026
Read

Valkey Keyspace Notifications in Production: Expired Events, hexpired and Cluster Mode

Run Valkey keyspace notifications in production: why expired and hexpired events lag the TTL, how to subscribe on every cluster node, and when a stream is safer.

Redis20 minNov 10, 2026
Read

Valkey GLIDE in Production: AZ-Affinity Reads and Migrating from Legacy Clients

Configure Valkey GLIDE AZ-affinity reads on servers and clients, learn what GLIDE 2.5.3 does not support, and migrate from redis-py, Jedis, ioredis, or go-redis one service at a time.

Redis20 minNov 10, 2026
Read