Redis error WRONGTYPE
WRONGTYPE Operation against a key holding the wrong kind of value
You ran a command for one data type on a key that holds another, such as GET on a hash. Nothing changed. Check the key with TYPE, then use that type’s commands or give the two kinds of data different key names.
WRONGTYPE Operation against a key holding the wrong kind of value
Tested on Redis 8.10.2 and Valkey 8.1.10 · Updated 9 October 2026
What it means
Every Redis key holds one type of value: a string, list, set, sorted set, hash or stream. Each
command works on one type: GET and INCR on strings, HGET and HGETALL on hashes, LPUSH and
LRANGE on lists, SADD and SMEMBERS on sets, ZADD and ZRANGE on sorted sets, XADD and
XRANGE on streams. Run one on a key of another type and Redis refuses with WRONGTYPE. The command
does nothing, and the key keeps its value.
A few things to know:
- A missing key is never the wrong type. Read commands treat it as empty (
GETreplies(nil)), and write commands create it with their own type. SEToverwrites a key of any type. It replaces a hash or list with a string without a warning. OnlySET … GET, which reads the old value first, fails withWRONGTYPE.- Some types are others underneath. Bitmaps and HyperLogLogs are strings, and geospatial
indexes are sorted sets, so
TYPEreportsstringandzset. A string that isn’t a HyperLogLog gets its own wording fromPFADD:WRONGTYPE Key is not a valid HyperLogLog string value. - Inside a Lua script, the error ends with the script it came from:
… script: d3c21d0c2b9ca22f82737626a27bcaf5d288f99f, on @user_script:1.
Common causes
- One key name used for two kinds of data. One part of your code caches a user as a JSON string
under
user:1; another stores the same user as a hash under the same name. Whichever writes first decides the type, and the other one fails. - The wrong command for the type:
GETon a hash,LRANGEon a set,HGETALLon a string. - A change of format. Your code switched from JSON strings to hashes (or back), and keys written in the old format are still there until they expire or are deleted.
- JSON documents. On Redis 8 with its JSON data type (and Redis Stack),
JSON.SETkeys have the typeReJSON-RL.GETandHGETALLon them fail withWRONGTYPE; read them withJSON.GET.
How to fix it
Check the key’s type
TYPE <key>
It replies string, list, set, zset, hash, stream (or a module’s type, such as
ReJSON-RL), or none if the key doesn’t exist. Then read it with that type’s commands:
TYPE | Read it with |
|---|---|
string | GET, MGET, STRLEN |
hash | HGETALL, HGET, HMGET |
list | LRANGE <key> 0 -1, LLEN |
set | SMEMBERS, SSCAN, SISMEMBER |
zset | ZRANGE <key> 0 -1 WITHSCORES, ZSCORE |
stream | XRANGE <key> - +, XREVRANGE |
ReJSON-RL | JSON.GET |
OBJECT ENCODING <key> shows how Redis stores the value internally (listpack, hashtable, int,
raw…). It’s useful for memory questions, but it’s TYPE that decides which commands work.
Find the keys with the unexpected type
SCAN takes a TYPE filter (Redis 6 and later), so you can list, say, every user:* key that is a
string when your code expects hashes:
SCAN 0 MATCH user:* TYPE string
Repeat with the cursor it returns until it comes back as 0.
Give each kind of data its own key names
Make the type part of the name, so two features can’t collide: user:1 for the hash,
user:1:cache for a cached JSON string, user:1:sessions for a set.
Replace old values on purpose
If the wrong-type keys are left over from an old format, delete them (DEL or UNLINK) or let them
expire, and let your code write them again in the new format. During a migration, code that can meet
both formats can check TYPE first and read accordingly. Overwriting with SET works on any type,
so only use it when you mean to replace the value.
Reproduce it
Redis 8.10.2, redis-cli 8.10.2, in database 15:
HSET seo:auth:user:1 name Ada email ada@example.com
(integer) 2
GET seo:auth:user:1
(error) WRONGTYPE Operation against a key holding the wrong kind of value
TYPE seo:auth:user:1
hash
OBJECT ENCODING seo:auth:user:1
"listpack"
HGETALL seo:auth:user:1
1) "name"
2) "Ada"
3) "email"
4) "ada@example.com"
LPUSH, INCR and SADD on the same hash got the same error, and so did HGET on a string,
LRANGE on a stream and SMEMBERS on a sorted set. SET seo:auth:user:1 x GET failed the same way,
while a plain SET on the hash replied OK and TYPE then said string. A key that didn’t exist:
TYPE replied none and GET replied (nil).
PFADD on a string holding 5:
(error) WRONGTYPE Key is not a valid HyperLogLog string value.
GET on the hash from inside a script (EVAL "return redis.call('GET', KEYS[1])" 1 seo:auth:user:1):
(error) WRONGTYPE Operation against a key holding the wrong kind of value script: d3c21d0c2b9ca22f82737626a27bcaf5d288f99f, on @user_script:1.
SCAN 0 MATCH seo:auth:* TYPE hash returned only the hash among the test keys. Valkey 8.1.10 gave
every message word for word.
The test server runs without Redis 8’s data-type modules, so the JSON case ran on a temporary Redis
8.10.2 server started the image’s default way, which loads them. After JSON.SET user:1 $ '{"name":"Ada"}', TYPE user:1 replied ReJSON-RL, and GET and HGETALL got WRONGTYPE. The
other way round, JSON.GET on a plain string replied (error) Existing key has wrong Redis type,
without the WRONGTYPE code.
In Inlet
The key grid shows each key’s type next to its TTL, size and value, so a namespace where one key
differs from the rest stands out; you can also filter the grid by type. Hashes show as JSON objects,
lists and sets as arrays, and sorted sets with their scores. Query tabs take redis-cli commands, so
you can run TYPE and the matching read command there. When the server refuses a command, Inlet
shows the message after the WRONGTYPE code and links to this page.