elunez/eladmin · error · BadRequestException

服务异常,请联系网站负责人

Error message

服务异常,请联系网站负责人

What it means

Thrown by VerifyServiceImpl.sendEmail when RedisUtils.set() returns false while trying to store a newly generated 6-digit email verification code under key `key + email`. The code was already generated from the email.ftl template engine, but the cache write failed, so the whole send is aborted with a generic 'service exception, contact the site owner' message. It is a BadRequestException (HTTP 400 surfaced via the global exception handler), masking an infrastructure failure in Redis.

Source

Thrown at eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java:60

    @Value("${code.expiration}")
    private Long expiration;
    private final RedisUtils redisUtils;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public EmailVo sendEmail(String email, String key) {
        EmailVo emailVo;
        String content;
        String redisKey = key + email;
        // 如果不存在有效的验证码,就创建一个新的
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
        Template template = engine.getTemplate("email.ftl");
        String oldCode =  redisUtils.get(redisKey, String.class);
        if(oldCode == null){
            String code = RandomUtil.randomNumbers (6);
            // 存入缓存
            if(!redisUtils.set(redisKey, code, expiration)){
                throw new BadRequestException("服务异常,请联系网站负责人");
            }
            content = template.render(Dict.create().set("code",code));
            // 存在就再次发送原来的验证码
        } else {
            content = template.render(Dict.create().set("code",oldCode));
        }
        emailVo = new EmailVo(Collections.singletonList(email),"ELADMIN后台管理系统",content);
        return emailVo;
    }

    @Override
    public void validated(String key, String code) {
        String value = redisUtils.get(key, String.class);
        if(!code.equals(value)){
            throw new BadRequestException("无效验证码");
        } else {
            redisUtils.del(key);
        }

View on GitHub (pinned to 55fbf70595)

Solutions

  1. Check Redis connectivity: verify spring.redis host/port/password in the running profile and test with redis-cli ping from the app host.
  2. Inspect Redis health: run INFO memory / INFO stats to check for maxmemory errors (used_memory > maxmemory, evicted_keys, rejected_connections).
  3. Look at the application log for the underlying Lettuce/Jedis exception thrown inside RedisUtils.set — the BadRequestException hides it.
  4. If Redis must be optional in some environment, wrap the sendEmail flow in a health check (e.g. redisUtils.get on a ping key) and return a specific 503-style error instead of this generic message.
  5. Improve the message or map the failure to a dedicated handler so operators can distinguish 'cache down' from other 400s.

Example fix

// before
if(!redisUtils.set(redisKey, code, expiration)){
    throw new BadRequestException("服务异常,请联系网站负责人");
}

// after: surface the infrastructure cause for logs while keeping a client-safe message
if(!redisUtils.set(redisKey, code, expiration)){
    log.error("Failed to write verify code to Redis, key={}", redisKey);
    throw new BadRequestException("验证码服务暂不可用,请稍后再试");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: confirm the cache backend answers before offering code sending
boolean cacheOk = false;
try {
    cacheOk = redisUtils.get("ping", String.class) != null || true; // any round-trip proves reachability
} catch (Exception ignore) { cacheOk = false; }
if (!cacheOk) {
    return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("验证码服务不可用");
}

Try / catch

try { verifyService.sendEmail(email, key); } catch (BadRequestException e) { if ("服务异常,请联系网站负责人".equals(e.getMessage())) { /* infra issue: alert ops, do NOT retry immediately */ } else { throw e; } }

Prevention

When it happens

Trigger: POST /api/code/sendEmail (or the equivalent verifyCode controller) for an email that has no unexpired code in Redis; redisUtils.set(redisKey, code, expiration) returns false — e.g. Redis is down, connection refused, maxmemory reached, or the setex fails. Any first-time code request while Redis is unhealthy produces this.

Common situations: Redis not started or wrong host/port/password in application.yml (spring.redis.*) in dev; Redis evicting keys under memory pressure so set fails; network partition between the app and Redis in containerized deployments; expiration misconfigured to an illegal value.

Related errors


AI-assisted analysis of elunez/eladmin@55fbf70595 (2026-08-14). Data as JSON: /api/errors/364c606403ccad0a. Report an issue: GitHub.