redis/redis-py · error · TokenRenewalErr

{e}

Error message

{e}

What it means

Raised as TokenRenewalErr from sync _renew_token (redis/auth/token_manager.py:296) wrapping any exception thrown by the user-supplied on_next callback during synchronous renewal. The message is str(original_exception). It surfaces caller-side bugs in the token-delivery callback rather than an IdP problem.

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 da03cdc7e8)

Solutions

  1. Inspect the wrapped exception (its message is the str of the original) to find the real cause in your on_next callback.
  2. Make on_next defensive: catch its own transient errors and avoid crashing the renewal loop.
  3. Register an on_error listener so renewal callback failures are reported rather than propagated.

Example fix

# before
def on_next(token):
    redis_client.execute_command('AUTH', token.get_value())  # may raise
listener.on_next = on_next
# after
def on_next(token):
    try:
        redis_client.execute_command('AUTH', token.get_value())
    except Exception as e:
        logging.warning('token apply failed: %s', e)
listener.on_next = on_next
Defensive patterns

Strategy: try-catch

Try / catch

from redis.auth.err import TokenRenewalErr
try:
    token_manager._renew_token()
except TokenRenewalErr as e:
    logger.exception('on_next callback failed during renewal: %s', e)

Prevention

When it happens

Trigger: The on_next callback set on the CredentialsListener raises while being invoked with a freshly renewed token (e.g. it tries to write to Redis and the connection is closed). The exception is re-wrapped and routed to on_error or re-raised.

Common situations: on_next callback performing I/O that fails (network down, Redis auth not yet applied); callback with a bug (KeyError/TypeError) processing the token object; callback storing the token in a closed resource.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/e8a2c05f35b372fd.json. Report an issue: GitHub.