MeWrite Docs

WRONGTYPE Operation against a key holding the wrong kind of value

Redisキーに対して不正な型の操作を実行した場合に発生するエラー

概要

WRONGTYPE Operation against a key holding the wrong kind of value は、Redisで特定のデータ型用のコマンドを、異なるデータ型のキーに対して実行した場合に発生するエラーです。

エラーメッセージ

(error) WRONGTYPE Operation against a key holding the wrong kind of value
redis.exceptions.ResponseError: WRONGTYPE Operation against a key holding the wrong kind of value

原因

  1. データ型の不一致: 文字列キーにリスト操作を実行など
  2. キーの再利用: 異なる型で同じキー名を使用
  3. コマンドの誤り: 間違ったコマンドを使用
  4. 古いデータ: 以前のデータが残っている

Redis のデータ型

データ型用途コマンド例
String文字列・数値GET, SET, INCR
List順序付きリストLPUSH, RPUSH, LRANGE
Set重複なしの集合SADD, SMEMBERS, SISMEMBER
Hashフィールドと値のペアHSET, HGET, HGETALL
Sorted Setスコア付き集合ZADD, ZRANGE, ZSCORE

解決策

1. キーの型を確認

1
2
3
4
5
6
7
8
9
# redis-cli でキーの型を確認
TYPE mykey

# 出力例
# string
# list
# set
# hash
# zset

2. 正しいコマンドを使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ❌ 間違い: 文字列に LPUSH
SET mykey "hello"
LPUSH mykey "world"  # WRONGTYPE エラー

# ✅ 正しい: リストに LPUSH
DEL mykey
LPUSH mykey "hello"
LPUSH mykey "world"

# ✅ 文字列には SET/GET
SET mykey "hello"
GET mykey

3. キーを削除してから再作成

1
2
3
4
5
6
7
8
# キーを削除
DEL mykey

# 新しい型で作成
LPUSH mylist "item1"

# または
HSET myhash field1 "value1"

4. 各データ型の正しい操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# String
SET user:1:name "Alice"
GET user:1:name
INCR counter

# List
LPUSH queue "task1"
RPUSH queue "task2"
LRANGE queue 0 -1

# Set
SADD tags "redis" "database"
SMEMBERS tags
SISMEMBER tags "redis"

# Hash
HSET user:1 name "Alice" age 30
HGET user:1 name
HGETALL user:1

# Sorted Set
ZADD leaderboard 100 "player1"
ZADD leaderboard 200 "player2"
ZRANGE leaderboard 0 -1 WITHSCORES

5. アプリケーションでの対処

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Python (redis-py)
import redis

r = redis.Redis(host='localhost', port=6379, db=0)

# キーの型を確認してから操作
key_type = r.type('mykey')

if key_type == b'string':
    value = r.get('mykey')
elif key_type == b'list':
    value = r.lrange('mykey', 0, -1)
elif key_type == b'hash':
    value = r.hgetall('mykey')
elif key_type == b'set':
    value = r.smembers('mykey')
elif key_type == b'zset':
    value = r.zrange('mykey', 0, -1, withscores=True)
elif key_type == b'none':
    # キーが存在しない
    pass
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Node.js (ioredis)
const Redis = require('ioredis');
const redis = new Redis();

async function safeGet(key) {
    const type = await redis.type(key);

    switch (type) {
        case 'string':
            return await redis.get(key);
        case 'list':
            return await redis.lrange(key, 0, -1);
        case 'hash':
            return await redis.hgetall(key);
        case 'set':
            return await redis.smembers(key);
        case 'zset':
            return await redis.zrange(key, 0, -1, 'WITHSCORES');
        case 'none':
            return null;
        default:
            throw new Error(`Unknown type: ${type}`);
    }
}

6. 名前空間でキーを分離

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# プレフィックスでデータ型を区別
# 文字列
SET str:user:1:name "Alice"

# ハッシュ
HSET hash:user:1 name "Alice" age 30

# リスト
LPUSH list:user:1:tasks "task1"

# セット
SADD set:user:1:tags "admin" "active"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Python でプレフィックスを使用
class RedisClient:
    def __init__(self):
        self.r = redis.Redis()

    def get_user(self, user_id):
        return self.r.hgetall(f'hash:user:{user_id}')

    def get_user_tasks(self, user_id):
        return self.r.lrange(f'list:user:{user_id}:tasks', 0, -1)

    def get_user_tags(self, user_id):
        return self.r.smembers(f'set:user:{user_id}:tags')

7. トランザクションでの対処

1
2
3
4
5
6
# MULTI/EXEC で安全に操作
MULTI
DEL mykey
LPUSH mykey "item1"
LPUSH mykey "item2"
EXEC
1
2
3
4
5
6
# Python でパイプライン
pipe = r.pipeline()
pipe.delete('mykey')
pipe.lpush('mykey', 'item1')
pipe.lpush('mykey', 'item2')
pipe.execute()

8. 開発時のデバッグ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 全キーを確認(本番では使用注意)
KEYS *

# パターンで検索
KEYS user:*

# スキャン(本番向け)
SCAN 0 MATCH user:* COUNT 100

# キーの情報
DEBUG OBJECT mykey

よくあるパターン

セッション管理

1
2
3
4
5
6
# ❌ 間違い: セッションを文字列で保存後、フィールドを追加しようとする
SET session:123 "user_id=1"
HSET session:123 user_id 1  # WRONGTYPE

# ✅ 正しい: 最初からハッシュを使用
HSET session:123 user_id 1 expires_at 1704067200

キャッシュ

1
2
3
4
5
6
7
# ❌ 間違い: リストキャッシュを文字列で上書き
LPUSH cache:items "item1"
SET cache:items "cached"  # 同じキーを別の型で使用

# ✅ 正しい: 異なるキー名を使用
LPUSH cache:items:list "item1"
SET cache:items:string "cached"

デバッグのコツ

モニタリング

1
2
3
4
5
# リアルタイムコマンド監視
MONITOR

# INFO でサーバー状態を確認
INFO keyspace

メモリ分析

1
2
3
4
5
# キーのメモリ使用量
MEMORY USAGE mykey

# 大きなキーを見つける
redis-cli --bigkeys

Redis の他のエラー

最終更新: 2025-12-08