{"record":{"id":"ff7ba4cefb914d0c","repo":"redis/redis-py","slug":"migrate-requires-at-least-one-key","errorCode":null,"errorMessage":"MIGRATE requires at least one key","messagePattern":"MIGRATE requires at least one key","errorType":"exception","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":1836,"sourceCode":"\n        The ``timeout``, specified in milliseconds, indicates the maximum\n        time the connection between the two servers can be idle before the\n        command is interrupted.\n\n        If ``copy`` is True, the specified ``keys`` are NOT deleted from\n        the source server.\n\n        If ``replace`` is True, this operation will overwrite the keys\n        on the destination server if they exist.\n\n        If ``auth`` is specified, authenticate to the destination server with\n        the password provided.\n\n        For more information, see https://redis.io/commands/migrate\n        \"\"\"\n        keys = list_or_args(keys, [])\n        if not keys:\n            raise DataError(\"MIGRATE requires at least one key\")\n        pieces = []\n        if copy:\n            pieces.append(b\"COPY\")\n        if replace:\n            pieces.append(b\"REPLACE\")\n        if auth:\n            pieces.append(b\"AUTH\")\n            pieces.append(auth)\n        pieces.append(b\"KEYS\")\n        pieces.extend(keys)\n        return self.execute_command(\n            \"MIGRATE\", host, port, \"\", destination_db, timeout, *pieces, **kwargs\n        )\n\n    @overload\n    def object(self: SyncClientProtocol, infotype: str, key: KeyT, **kwargs) -> Any: ...\n\n    @overload","sourceCodeStart":1818,"sourceCodeEnd":1854,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/commands/core.py#L1818-L1854","documentation":"Raised by migrate() (redis/commands/core.py:1836) when the keys argument normalizes (via list_or_args) to an empty list. MIGRATE with no keys is invalid at the protocol level, so the library rejects it before sending. This is a redis.exceptions.DataError raised client-side.","triggerScenarios":"Calling r.migrate(host, port, [], dest_db, timeout) with an empty list, or r.migrate(host, port, \"\", dest_db, timeout), or passing a variable that evaluates to no keys after list_or_args expansion.","commonSituations":"Migrating a dynamic key set that happens to be empty for a given shard/partition; filtering keys down to none before migrating.","solutions":["Guard the call: if keys: r.migrate(host, port, keys, dest_db, timeout)","Pass at least one key as a list: r.migrate(host, port, [\"k1\"], dest_db, timeout)","list_or_args also accepts a single key string, so r.migrate(host, port, \"k1\", dest_db, timeout) works"],"exampleFix":"# before\nr.migrate(host, port, selected_keys, dest_db, timeout)\n# after\nif selected_keys:\n    r.migrate(host, port, selected_keys, dest_db, timeout)","handlingStrategy":"validation","validationCode":"keys = list_or_args(keys, []) if not isinstance(keys, list) else keys\nif not keys:\n    # nothing to migrate; skip\n    return\nr.migrate(host, port, keys, destination_db, timeout)","typeGuard":null,"tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    r.migrate(host, port, keys, dest_db, timeout)\nexcept DataError as e:\n    if \"requires at least one key\" in str(e):\n        pass  # empty key set is a no-op\n    else:\n        raise","preventionTips":["Guard migrate() with 'if keys:' before calling.","Filter key sets before migration and treat empty as a skip.","Remember list_or_args accepts a single string OR a list."],"tags":["migration","validation","dataerror"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}