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

  1. Catch CodeExpired and prompt the user to request a new code (call gen_and_send_async after cooldown)
  2. Verify the cache backend (CACHES/Redis URL) is shared and persistent across app instances
  3. Increase TTL to a realistic value (e.g. 5 minutes) accounting for SMS delivery latency
  4. 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

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.