{"record":{"id":"cb33268cf0b4b1b7","repo":"jumpserver/jumpserver","slug":"code-expired","errorCode":null,"errorMessage":"Code expired","messagePattern":"Code expired","errorType":"exception","errorClass":"CodeExpired","httpStatus":null,"severity":"warning","filePath":"apps/common/utils/verify_code.py","lineNumber":62,"sourceCode":"        if ttl > 0:\n            logger.warning('Send sms too frequently, delay {}'.format(ttl))\n            raise CodeSendTooFrequently(ttl)\n\n        return self.gen_and_send()\n\n    def gen_and_send(self):\n        try:\n            if not self.code:\n                self.code = self.__generate()\n            self.__send(self.code)\n        except JMSException:\n            self.__clear()\n            raise\n\n    def verify(self, code):\n        right = cache.get(self.key)\n        if not right:\n            raise CodeExpired\n\n        if right != code:\n            raise CodeError\n\n        self.__clear()\n        return True\n\n    def __clear(self):\n        cache.delete(self.key)\n\n    def __ttl(self):\n        return cache.ttl(self.key)\n\n    def __get_code(self):\n        return cache.get(self.key)\n\n    def __generate(self):\n        code = random_string(settings.SMS_CODE_LENGTH, lower=False, upper=False)","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/jumpserver/jumpserver/blob/6ec464fabd61b95912d539455a3a5f15f5c59fe0/apps/common/utils/verify_code.py#L44-L80","documentation":"VerifyCode.verify() in apps/common/utils/verify_code.py reads the expected code from cache; if cache.get(self.key) is falsy — key expired (TTL elapsed) or never written — it raises CodeExpired. The code has a finite lifetime and verification after expiry fails regardless of correctness.","triggerScenarios":"User enters the code after the TTL elapsed; the SMS was delayed in delivery so the user submits late; cache was flushed/restarted (Redis restart, cache clear) between send and verify; send itself failed so no code was ever cached.","commonSituations":"Slow SMS gateways; user idle on the input screen past expiry; shared/dev environments with cache flushing; misconfigured short TTL; load-balanced app servers pointing at different cache backends.","solutions":["Catch CodeExpired and prompt the user to request a new code (call gen_and_send_async after cooldown)","Verify the cache backend (CACHES/Redis URL) is shared and persistent across app instances","Increase TTL to a realistic value (e.g. 5 minutes) accounting for SMS delivery latency","Add telemetry on send->verify latency to detect gateway delays"],"exampleFix":"# before\nverify_code.verify(user_input)\n# after\nfrom apps.common.utils.verify_code import CodeExpired, CodeError\ntry:\n    verify_code.verify(user_input)\nexcept CodeExpired:\n    return error_response('Code expired, please request a new one')\nexcept CodeError:\n    return error_response('Wrong code')","handlingStrategy":"try-catch","validationCode":"from django.core.cache import cache\n\ndef code_alive(key) -> bool:\n    return cache.get(key) is not None\n\nif not code_alive(verify_code.key):\n    return error_response('Code expired or never sent; request a new one')\nverify_code.verify(user_code)","typeGuard":null,"tryCatchPattern":"from apps.common.utils.verify_code import CodeExpired, CodeError\ntry:\n    verify_code.verify(user_code)\nexcept CodeExpired:\n    return error_response('Code expired, please resend')\nexcept CodeError:\n    return error_response('Incorrect code')","preventionTips":["Show the code's remaining validity time in the UI","Ensure all app instances share the same cache backend","Send and verify use the same key derivation/params — never regenerate keys ad hoc"],"tags":["python","verification-code","cache-ttl","expiration"],"backgroundTag":"verification-code-expired","analyzedSha":"6ec464fabd61b95912d539455a3a5f15f5c59fe0","analyzedAt":"2026-08-28T11:33:00.925Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}