{"id":"025f42546a7b61e1","repo":"redis/redis-py","slug":"requested-token-is-expired","errorCode":null,"errorMessage":"Requested token is expired","messagePattern":"Requested token is expired","errorType":"exception","errorClass":"TokenRenewalErr","httpStatus":null,"severity":"error","filePath":"redis/auth/token_manager.py","lineNumber":284,"sourceCode":"            - refresh_before\n            - (datetime.now(timezone.utc).timestamp() * 1000)\n        )\n\n    def _renew_token(self, skip_initial: bool = False):\n        \"\"\"\n        Task to renew token from identity provider.\n        Schedules renewal tasks based on token TTL.\n        \"\"\"\n\n        try:\n            token_res = self.acquire_token(force_refresh=True)\n            delay = self._calculate_renewal_delay(\n                token_res.get_token().get_expires_at_ms(),\n                token_res.get_token().get_received_at_ms(),\n            )\n\n            if token_res.get_token().is_expired():\n                raise TokenRenewalErr(\"Requested token is expired\")\n\n            if self._listener.on_next is None:\n                logger.warning(\n                    \"No registered callback for token renewal task. Renewal cancelled\"\n                )\n                return\n\n            if not skip_initial:\n                try:\n                    self._listener.on_next(token_res.get_token())\n                except Exception as e:\n                    raise TokenRenewalErr(e)\n\n            if delay <= 0:\n                return\n\n            loop = asyncio.get_running_loop()\n            self._next_timer = loop.call_later(delay, self._renew_token)","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/auth/token_manager.py#L266-L302","documentation":"Raised as TokenRenewalErr from the sync _renew_token (redis/auth/token_manager.py:284) after acquire_token(force_refresh=True) returns a token whose is_expired() is True. The identity provider handed back a token that is already past its expiry, so renewal produced nothing usable. If an on_error listener is registered the error is routed there, otherwise it is re-raised.","triggerScenarios":"A scheduled/sync token renewal where the IdP's response token's exp is already in the past; significant clock skew between client and IdP; IdP misconfiguration issuing instantly-expiring tokens.","commonSituations":"Clock skew (client clock ahead of IdP); IdP bug returning expired tokens; long network delay between token issue and receipt; wrong exp units in the IdP response.","solutions":["Check for clock skew between the client and the identity provider; synchronize NTP.","Inspect the token's exp vs current time to confirm the IdP is issuing valid-lifetime tokens.","Register an on_error listener to handle renewal failures gracefully and trigger re-auth.","Retry renewal once after a short delay in case the issue is transient."],"exampleFix":"# before\n# token_manager renews and IdP returns already-expired token -> TokenRenewalErr\n# after\n# register an on_error handler and verify IdP exp / clock sync\nlistener.on_error = lambda e: logging.error('renewal failed: %s', e)","handlingStrategy":"try-catch","validationCode":"import time\ntoken_exp = decoded_jwt.get('exp')\nif token_exp is not None and token_exp * 1000 <= time.time() * 1000:\n    logger.warning('IdP returned an already-expired token')","typeGuard":"def token_already_expired(token) -> bool:\n    return token.is_expired()","tryCatchPattern":"from redis.auth.err import TokenRenewalErr\ntry:\n    token_manager._renew_token()\nexcept TokenRenewalErr as e:\n    logger.error('sync renewal returned expired token: %s', e)\n    reauthenticate()","preventionTips":["Sync clocks via NTP on all hosts running token renewal.","Register an on_error listener to capture renewal failures."],"tags":["auth","token","expiration"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}