{"id":"b16da8bde9cf8249","repo":"redis/redis-py","slug":"key-must-be-either-a-string-or-bytes","errorCode":null,"errorMessage":"Key must be either a string or bytes","messagePattern":"Key must be either a string or bytes","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"redis/utils.py","lineNumber":355,"sourceCode":"        for _ in range(diff):\n            num_versions1.append(0)\n\n    for i, ver in enumerate(num_versions1):\n        if num_versions1[i] > num_versions2[i]:\n            return -1\n        elif num_versions1[i] < num_versions2[i]:\n            return 1\n\n    return 0\n\n\ndef ensure_string(key):\n    if isinstance(key, bytes):\n        return key.decode(\"utf-8\")\n    elif isinstance(key, str):\n        return key\n    else:\n        raise TypeError(\"Key must be either a string or bytes\")\n\n\ndef extract_expire_flags(\n    ex: Optional[ExpiryT] = None,\n    px: Optional[ExpiryT] = None,\n    exat: Optional[AbsExpiryT] = None,\n    pxat: Optional[AbsExpiryT] = None,\n) -> List[EncodableT]:\n    exp_options: list[EncodableT] = []\n    if ex is not None:\n        exp_options.append(\"EX\")\n        if isinstance(ex, datetime.timedelta):\n            exp_options.append(int(ex.total_seconds()))\n        elif isinstance(ex, int):\n            exp_options.append(ex)\n        elif isinstance(ex, str) and ex.isdigit():\n            exp_options.append(int(ex))\n        else:","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/utils.py#L337-L373","documentation":"Raised by ensure_string (redis/utils.py:355) as a built-in TypeError. The helper normalizes a key to str: it decodes bytes, returns str unchanged, and rejects anything else. Passing a non-str/non-bytes value (int, float, None, bool, custom object) raises TypeError('Key must be either a string or bytes'). It is a strict input-type guard used by internal normalization paths.","triggerScenarios":"Calling ensure_string with a value that is neither str nor bytes, e.g. ensure_string(123), ensure_string(None), ensure_string(1.5), or ensure_string(some_object). This typically surfaces from application code that pipes unconverted identifiers or numeric keys through code paths that end up in ensure_string.","commonSituations":"Numeric primary keys used directly as Redis keys without str() conversion; None passed due to a missing dict lookup; ORM/model objects whose __str__ is relied upon but not invoked; JSON-decoded integer keys not normalized.","solutions":["Convert the value before calling: ensure_string(str(key)) for ints, or encode to bytes.","Validate key type at the API boundary and reject/coerce non-string types early.","For numeric keys, prefer a consistent key-building helper, e.g. f'user:{id}'."],"exampleFix":"# before\nensure_string(user_id)        # user_id is an int -> TypeError\n# after\nensure_string(str(user_id))   # or build a namespaced key\nensure_string(f'user:{user_id}')","handlingStrategy":"type-guard","validationCode":"from typing import Any\n\ndef ensure_string_safe(key: Any) -> str:\n    if isinstance(key, bytes):\n        return key.decode('utf-8')\n    if isinstance(key, str):\n        return key\n    # Coerce ints/floats/Decimal instead of raising\n    if isinstance(key, (int, float)):\n        return str(key)\n    raise TypeError(f'Key must be str, bytes, or a number; got {type(key).__name__}')","typeGuard":"from typing import Any\n\ndef is_valid_key(key: Any) -> bool:\n    return isinstance(key, (str, bytes))\n\n# Usage\nif not is_valid_key(maybe_key):\n    maybe_key = str(maybe_key)","tryCatchPattern":"try:\n    normalized = ensure_string(value)\nexcept TypeError:\n    normalized = str(value)","preventionTips":["Centralize key construction in a helper that always returns str/bytes (e.g. f'user:{id}').","Validate input types at API boundaries and coerce early.","Never pass None or raw ORM objects as keys.","Add unit tests for the key-building path with int/float/None inputs."],"tags":["types","validation","keys","input"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}