{"record":{"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/6a6b581b48225afa0b76912d1028c6035baee932/redis/auth/token_manager.py#L266-L302","documentation":"Raised as TokenRenewalErr inside the synchronous _renew_token after force-refreshing the token from the identity provider when token_res.get_token().is_expired() still returns True. This means the freshly acquired token is already expired (e.g. severe clock skew, or the IdP issued an already-expired token). The renewal loop cannot schedule future work against a dead token, so it aborts.","triggerScenarios":"The synchronous token manager's _renew_token calls acquire_token(force_refresh=True), then checks is_expired() on the result; if True, it raises this. Reached during scheduled renewal or the initial token fetch when skip_initial is False.","commonSituations":"Clock skew between the client and the identity provider so a just-issued token appears expired. The IdP misconfiguration issues tokens with exp in the past. Very short TTLs combined with acquisition latency. System clock not synced via NTP.","solutions":["Sync the system clock (NTP/chrony) to eliminate clock skew against the IdP.","Verify the IdP token TTL and expiration configuration.","Check network latency between client and IdP; increase TTL if acquisition is slow.","Register an on_error listener to handle TokenRenewalErr gracefully and trigger re-auth."],"exampleFix":"# before: clock skew makes new token look expired\n# (no code change; fix the host clock)\n# after\nsudo chronyc makestep  # or ntpdate / timedatectl set-ntp true","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from redis.auth.err import TokenRenewalErr\ntry:\n    manager.start()\nexcept TokenRenewalErr as e:\n    if \"expired\" in str(e).lower():\n        logger.error(\"newly issued token is expired; check clock sync\")\n    raise","preventionTips":["Keep system clocks synced via NTP to avoid phantom-expired tokens.","Register an on_error listener to handle renewal failures with backoff."],"tags":["auth","token-renewal","clock-skew","sync"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}