jumpserver/jumpserver · warning · CodeExpired
Code expired
Error message
Code expired
What it means
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.
Source
Thrown at apps/common/utils/verify_code.py:62
if ttl > 0:
logger.warning('Send sms too frequently, delay {}'.format(ttl))
raise CodeSendTooFrequently(ttl)
return self.gen_and_send()
def gen_and_send(self):
try:
if not self.code:
self.code = self.__generate()
self.__send(self.code)
except JMSException:
self.__clear()
raise
def verify(self, code):
right = cache.get(self.key)
if not right:
raise CodeExpired
if right != code:
raise CodeError
self.__clear()
return True
def __clear(self):
cache.delete(self.key)
def __ttl(self):
return cache.ttl(self.key)
def __get_code(self):
return cache.get(self.key)
def __generate(self):
code = random_string(settings.SMS_CODE_LENGTH, lower=False, upper=False)View on GitHub (pinned to 6ec464fabd)
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
Example fix
# before
verify_code.verify(user_input)
# after
from apps.common.utils.verify_code import CodeExpired, CodeError
try:
verify_code.verify(user_input)
except CodeExpired:
return error_response('Code expired, please request a new one')
except CodeError:
return error_response('Wrong code') Defensive patterns
Strategy: try-catch
Validate before calling
from django.core.cache import cache
def code_alive(key) -> bool:
return cache.get(key) is not None
if not code_alive(verify_code.key):
return error_response('Code expired or never sent; request a new one')
verify_code.verify(user_code) Try / catch
from apps.common.utils.verify_code import CodeExpired, CodeError
try:
verify_code.verify(user_code)
except CodeExpired:
return error_response('Code expired, please resend')
except CodeError:
return error_response('Incorrect code') Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of jumpserver/jumpserver@6ec464fabd (2026-08-28).
Data as JSON: /api/errors/cb33268cf0b4b1b7.
Report an issue: GitHub.