{"id":"e8a2c05f35b372fd","repo":"redis/redis-py","slug":"e","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"exception","errorClass":"TokenRenewalErr","httpStatus":null,"severity":"error","filePath":"redis/auth/token_manager.py","lineNumber":296,"sourceCode":"            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)\n            logger.info(f\"Next token renewal scheduled in {delay} seconds\")\n            return token_res\n        except Exception as e:\n            if self._listener.on_error is None:\n                raise e\n\n            self._listener.on_error(e)\n\n    async def _renew_token_async(\n        self, skip_initial: bool = False, init_event: asyncio.Event = None\n    ):\n        \"\"\"","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/auth/token_manager.py#L278-L314","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped exception (its message is the str of the original) to find the real cause in your on_next callback.","Make on_next defensive: catch its own transient errors and avoid crashing the renewal loop.","Register an on_error listener so renewal callback failures are reported rather than propagated."],"exampleFix":"# before\ndef on_next(token):\n    redis_client.execute_command('AUTH', token.get_value())  # may raise\nlistener.on_next = on_next\n# after\ndef on_next(token):\n    try:\n        redis_client.execute_command('AUTH', token.get_value())\n    except Exception as e:\n        logging.warning('token apply failed: %s', e)\nlistener.on_next = on_next","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from redis.auth.err import TokenRenewalErr\ntry:\n    token_manager._renew_token()\nexcept TokenRenewalErr as e:\n    logger.exception('on_next callback failed during renewal: %s', e)","preventionTips":["Make on_next callbacks defensive — catch their own transient errors.","Register on_error so callback failures are reported, not fatal."],"tags":["auth","token","callback"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}