{"record":{"id":"6ab27e155a2a4ab3","repo":"redis/redis-py","slug":"async-redis-client-does-not-support-class-inclusio","errorCode":null,"errorMessage":"Async Redis client does not support class inclusion","messagePattern":"Async Redis client does not support class inclusion","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":4812,"sourceCode":"        \"\"\"\n        pieces: list[str | int] = [key1, key2]\n        if len:\n            pieces.append(\"LEN\")\n        if idx:\n            pieces.append(\"IDX\")\n        if minmatchlen is not None and minmatchlen != 0:\n            pieces.extend([\"MINMATCHLEN\", minmatchlen])\n        if withmatchlen:\n            pieces.append(\"WITHMATCHLEN\")\n        return self.execute_command(\"LCS\", *pieces, keys=[key1, key2])\n\n\nclass AsyncBasicKeyCommands(BasicKeyCommands):\n    def __delitem__(self, name: KeyT):\n        raise TypeError(\"Async Redis client does not support class deletion\")\n\n    def __contains__(self, name: KeyT):\n        raise TypeError(\"Async Redis client does not support class inclusion\")\n\n    def __getitem__(self, name: KeyT):\n        raise TypeError(\"Async Redis client does not support class retrieval\")\n\n    def __setitem__(self, name: KeyT, value: EncodableT):\n        raise TypeError(\"Async Redis client does not support class assignment\")\n\n    async def watch(self, *names: KeyT) -> None:\n        return super().watch(*names)\n\n    async def unwatch(self) -> None:\n        return super().unwatch()\n\n\nclass ListCommands(CommandsProtocol):\n    \"\"\"\n    Redis commands for List data type.\n    see: https://redis.io/topics/data-types#lists","sourceCodeStart":4794,"sourceCodeEnd":4830,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/commands/core.py#L4794-L4830","documentation":"Raised by AsyncBasicKeyCommands.__contains__ (a TypeError) when you use 'key in async_client' on an async Redis client. The sync client would map this to EXISTS, but __contains__ cannot return an awaitable, so the async client forbids it to avoid returning a coroutine that would always be truthy.","triggerScenarios":"Writing \"if 'mykey' in client:\" or \"'mykey' in client\" where client is a redis.asyncio.Redis instance.","commonSituations":"Porting sync code that checks key existence via 'in'. The 'in' operator on an awaitable would silently always evaluate to True (coroutines are truthy), which is why the library blocks it.","solutions":["Replace \"key in client\" with \"bool(await client.exists(key))\".","For multiple keys, use the returned count from exists()."],"exampleFix":"# before\nif 'mykey' in client:\n    ...\n\n# after\nif await client.exists('mykey'):\n    ...","handlingStrategy":"validation","validationCode":"# Never use 'in' on an async client; call exists explicitly.\nexists = bool(await client.exists(key))  # instead of 'key in client'","typeGuard":"import redis\n\ndef is_async_client(c) -> bool:\n    return isinstance(c, (redis.asyncio.Redis, redis.asyncio.RedisCluster))","tryCatchPattern":"try:\n    _ = key in client\nexcept TypeError as e:\n    if 'class inclusion' in str(e):\n        result = bool(await client.exists(key))\n    else:\n        raise","preventionTips":["Replace 'key in client' with 'await client.exists(key)' everywhere in async code.","Beware: without this guard, 'key in async_client' returns a coroutine that is always truthy — a silent bug."],"tags":["async","dunder","contains","exists","typeerror","sync-to-async"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}