{"record":{"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/6a6b581b48225afa0b76912d1028c6035baee932/redis/auth/token_manager.py#L278-L314","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped exception e (the TokenRenewalErr cause) to find the real failure in on_next.","Make the on_next callback defensive: catch and handle its own errors so renewal is not aborted.","If on_error is registered, the manager routes it there instead of raising — register an on_error handler."],"exampleFix":"# before\ndef on_next(token):\n    redis.auth(token.get_value())  # raises if connection closed\n# after\ndef on_next(token):\n    try:\n        redis.auth(token.get_value())\n    except Exception:\n        logger.exception(\"failed to apply renewed token\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"def on_next(token):\n    try:\n        apply_token(token)\n    except Exception:\n        logger.exception(\"on_next failed; renewal will be aborted\")\n        raise","preventionTips":["Make on_next callbacks defensive so transient apply failures do not kill renewal.","Register on_error to route failures instead of propagating."],"tags":["auth","token-renewal","callback","sync"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}