redis/redis-py · error · TokenRenewalErr

{e}

Error message

{e}

What it means

Raised as TokenRenewalErr(e) wrapping whatever exception the user-supplied on_next callback threw during synchronous token renewal. _renew_token calls self._listener.on_next(token) to deliver the renewed token; if that callback raises, the error is re-raised as a TokenRenewalErr. The original exception e is preserved as the message/cause.

Solutions

  1. Inspect the wrapped exception e (the TokenRenewalErr cause) to find the real failure in on_next.
  2. Make the on_next callback defensive: catch and handle its own errors so renewal is not aborted.
  3. If on_error is registered, the manager routes it there instead of raising — register an on_error handler.

Example fix

# before
def on_next(token):
    redis.auth(token.get_value())  # raises if connection closed
# after
def on_next(token):
    try:
        redis.auth(token.get_value())
    except Exception:
        logger.exception("failed to apply renewed token")
Defensive patterns

Strategy: try-catch

Try / catch

def on_next(token):
    try:
        apply_token(token)
    except Exception:
        logger.exception("on_next failed; renewal will be aborted")
        raise

Prevention

When it happens

Trigger: Registering an on_next listener on the token manager whose body raises (e.g. fails to persist the token, calls a failing Redis AUTH, or has a bug). The sync renewal path invokes it and wraps the failure in TokenRenewalErr.

Common situations: A callback that applies the new token via AUTH but the connection is closed. A callback with a bug or that hits a downstream service error. Persisting the token to disk/storage that is full or read-only.

Related errors


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

Appendix: source

Thrown at redis/auth/token_manager.py:296

            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)
            logger.info(f"Next token renewal scheduled in {delay} seconds")
            return token_res
        except Exception as e:
            if self._listener.on_error is None:
                raise e

            self._listener.on_error(e)

    async def _renew_token_async(
        self, skip_initial: bool = False, init_event: asyncio.Event = None
    ):
        """

View on GitHub (pinned to 6a6b581b48)