yikart/AiToEarn · warning · AppHttpException

ErrHttpBack.err_user_code_had

ErrHttpBack.err_user_code_had

Error message

err_user_code_had

What it means

AppHttpException with ErrHttpBack.err_user_code_had (errCode 40019, message '验证码未过期'). Thrown by LoginService.postPhoneRegisterCode (login.service.ts:34) when a verification code for this phone is still cached in Redis under key `Password:<phone>`, i.e. the previous code has not expired (5-minute TTL). Rate-limits SMS code requests.

Source

Thrown at project/aitoearn-electron/server/src/user/login.service.ts:34

  Password = 'phone_register_code',
  Code = 'phone_login_code',
  PhoneAuth = 'phone_login_auth_token', // 手机号一键登录
}
@Injectable()
export class LoginService {
  constructor(
    private readonly redisService: RedisService,
    private readonly alicloudSmsService: AlicloudSmsService,
  ) {}

  /**
   * 发送手机号注册的验证码
   * @param phone
   */
  async postPhoneRegisterCode(phone: string) {
    const cacheKey = `${LoginTypeCacheKey.Password}:${phone}`;
    let code = await this.redisService.get(cacheKey);
    if (code) throw new AppHttpException(ErrHttpBack.err_user_code_had);

    code = getRandomString(6, true);
    const res = await this.alicloudSmsService.sendLoginSms(phone, code);

    if (process.env.NODE_ENV === 'production') {
      if (!res) throw new AppHttpException(ErrHttpBack.err_user_code_send_fail);
    }

    this.redisService.setKey(cacheKey, code, 60 * 5);
    console.log('发送短信成功', code);

    return process.env.NODE_ENV === 'production' ? res : code;
  }

  /**
   * 发送手机号登录的验证码
   * @param phone
   */

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Wait until the existing code expires (60*5s TTL from issue time) before requesting a new one
  2. Use the still-valid cached code instead of requesting a new one
  3. In tests, delete the Redis key `Password:<phone>` (or flush the test Redis) between runs
  4. Disable the resend button client-side for the 5-minute cooldown window

Example fix

// before
await api.postPhoneRegisterCode(phone) // throws 40019 if code cached
// after
try {
  await api.postPhoneRegisterCode(phone)
} catch (e) {
  if (e.errCode === '40019') { /* reuse existing code or wait for TTL */ }
}
Defensive patterns

Strategy: retry

Validate before calling

const cooldownKey = `sms-cooldown:${phone}`
if (await cache.get(cooldownKey)) throw new Error('Please wait for the previous code to expire')
await api.postPhoneRegisterCode(phone)
await cache.set(cooldownKey, '1', 300)

Type guard

null

Try / catch

try {
  await api.postPhoneRegisterCode(phone)
} catch (e) {
  if (e?.errCode === '40019') {
    // code still valid: prompt user to check SMS or wait out the 5-minute TTL
  } else throw e
}

Prevention

When it happens

Trigger: POST phone register code for a phone number whose Redis key `${LoginTypeCacheKey.Password}:${phone}` still holds an unexpired code — requesting a second code within the 5-minute window.

Common situations: User clicks 'send code' twice; automated test reuses the same phone number without clearing Redis; Redis TTL not respected by a client that retries on UI error; shared test phone across environments with a common Redis.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/8be7bd19113ed848. Report an issue: GitHub.