redis/redis-py · error · TokenRenewalErr

Requested token is expired

Error message

Requested token is expired

What it means

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.

Solutions

  1. Sync the system clock (NTP/chrony) to eliminate clock skew against the IdP.
  2. Verify the IdP token TTL and expiration configuration.
  3. Check network latency between client and IdP; increase TTL if acquisition is slow.
  4. Register an on_error listener to handle TokenRenewalErr gracefully and trigger re-auth.

Example fix

# before: clock skew makes new token look expired
# (no code change; fix the host clock)
# after
sudo chronyc makestep  # or ntpdate / timedatectl set-ntp true
Defensive patterns

Strategy: retry

Try / catch

from redis.auth.err import TokenRenewalErr
try:
    manager.start()
except TokenRenewalErr as e:
    if "expired" in str(e).lower():
        logger.error("newly issued token is expired; check clock sync")
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/025f42546a7b61e1. Report an issue: GitHub.

Appendix: source

Thrown at redis/auth/token_manager.py:284

            - refresh_before
            - (datetime.now(timezone.utc).timestamp() * 1000)
        )

    def _renew_token(self, skip_initial: bool = False):
        """
        Task to renew token from identity provider.
        Schedules renewal tasks based on token TTL.
        """

        try:
            token_res = self.acquire_token(force_refresh=True)
            delay = self._calculate_renewal_delay(
                token_res.get_token().get_expires_at_ms(),
                token_res.get_token().get_received_at_ms(),
            )

            if token_res.get_token().is_expired():
                raise TokenRenewalErr("Requested token is expired")

            if self._listener.on_next is None:
                logger.warning(
                    "No registered callback for token renewal task. Renewal cancelled"
                )
                return

            if not skip_initial:
                try:
                    self._listener.on_next(token_res.get_token())
                except Exception as e:
                    raise TokenRenewalErr(e)

            if delay <= 0:
                return

            loop = asyncio.get_running_loop()
            self._next_timer = loop.call_later(delay, self._renew_token)

View on GitHub (pinned to 6a6b581b48)