{"record":{"id":"975cf63ef94737eb","repo":"redis/redis-py","slug":"invalid-username-or-password","errorCode":null,"errorMessage":"Invalid Username or Password","messagePattern":"Invalid Username or Password","errorType":"exception","errorClass":"AuthenticationError","httpStatus":null,"severity":"critical","filePath":"redis/asyncio/connection.py","lineNumber":993,"sourceCode":"            ) != int(self.protocol):\n                raise ConnectionError(\"Invalid RESP version\")\n        # avoid checking health here -- PING will fail if we try\n        # to check the health prior to the AUTH\n        elif auth_args:\n            await self.send_command(\"AUTH\", *auth_args, check_health=False)\n\n            try:\n                auth_response = await self.read_response()\n            except AuthenticationWrongNumberOfArgsError:\n                # a username and password were specified but the Redis\n                # server seems to be < 6.0.0 which expects a single password\n                # arg. retry auth with just the password.\n                # https://github.com/andymccurdy/redis-py/issues/1274\n                await self.send_command(\"AUTH\", auth_args[-1], check_health=False)\n                auth_response = await self.read_response()\n\n            if str_if_bytes(auth_response) != \"OK\":\n                raise AuthenticationError(\"Invalid Username or Password\")\n\n        # if resp version is specified, switch to it\n        elif check_protocol_version(self.protocol, 3):\n            if isinstance(self._parser, _AsyncRESP2Parser):\n                self.set_parser(_AsyncRESP3Parser)\n                # update cluster exception classes\n                self._parser.EXCEPTION_CLASSES = parser.EXCEPTION_CLASSES\n                self._parser.on_connect(self)\n            await self.send_command(\"HELLO\", self.protocol, check_health=check_health)\n            response = await self.read_response()\n            # if response.get(b\"proto\") != self.protocol and response.get(\n            #     \"proto\"\n            # ) != self.protocol:\n            #     raise ConnectionError(\"Invalid RESP version\")\n\n        # Activate maintenance notifications for this connection\n        # if enabled in the configuration\n        # This is a no-op if maintenance notifications are not enabled","sourceCodeStart":975,"sourceCodeEnd":1011,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/connection.py#L975-L1011","documentation":"Raised as AuthenticationError during the async connection handshake (on_connect) after the AUTH command's response is not the literal string 'OK'. The library sends AUTH with the configured username/password (retrying with just the password if the server rejects the arg count, indicating Redis < 6.0) and treats any non-OK reply as failed credentials. This is a hard failure: the connection is never returned to the pool healthy.","triggerScenarios":"Constructing redis.asyncio.Redis(... username=..., password=...) or from_url('redis://user:wrongpass@host') and issuing any command, which forces on_connect() -> AUTH. Also fires when a Redis 6+ ACL user lacks permission or the password is stale/wrong.","commonSituations":"Wrong password in env var/secret; rotated credentials not yet picked up; connecting with a username to a Redis < 6.0 server that uses legacy requirepass (the single-arg retry path); ACL user configured without +@all or with a denied password; copy-paste error including the URL-encoded percent in the literal password.","solutions":["Verify the AUTH credentials directly with redis-cli -u '<url>' AUTH against the same endpoint.","Confirm the password does not contain URL-special characters that need percent-encoding when passed via redis://user:pass@host; either percent-encode or pass username=/password= kwargs instead.","Check the server's ACL LIST / CONFIG GET requirepass to confirm the user exists and the password matches.","If the server is Redis < 6.0, pass only password= (no username) so the legacy single-arg AUTH path is taken.","Rotate/refresh the credential source if using a token provider (e.g. EntraID) and re-create the client."],"exampleFix":"// before\nr = redis.asyncio.from_url('redis://default:pa$$w0rd@host:6379')\n// after\nr = redis.asyncio.Redis(host='host', username='default', password='pa$$w0rd')","handlingStrategy":"try-catch","validationCode":"import redis.asyncio as aioredis\n\nasync def probe_auth(url: str) -> bool:\n    r = aioredis.from_url(url, socket_connect_timeout=2)\n    try:\n        await r.ping()\n        return True\n    except aioredis.AuthenticationError:\n        return False\n    finally:\n        await r.aclose()","typeGuard":"from redis.exceptions import AuthenticationError\n\ndef is_auth_error(exc: BaseException) -> bool:\n    return isinstance(exc, AuthenticationError)","tryCatchPattern":"import redis.asyncio as aioredis\nfrom redis.exceptions import AuthenticationError\n\ntry:\n    await client.ping()\nexcept AuthenticationError:\n    # credentials wrong: do NOT retry blindly; refresh creds then recreate client\n    client = await rebuild_client_with_fresh_credentials()","preventionTips":["Validate credentials once at startup with a PING probe before entering the hot path.","Pass username/password as kwargs (not URL-encoded) to avoid percent-encoding mistakes.","For Redis < 6.0, pass password only (no username) so the legacy AUTH path is used.","Use a secret manager / token provider so rotated creds are reloaded automatically."],"tags":["auth","connection","async","credentials","handshake"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}