yikart/AiToEarn · error · AppHttpException

ErrHttpBack.err_user_code_send_fail

ErrHttpBack.err_user_code_send_fail

Error message

err_user_code_send_fail

What it means

AppHttpException with ErrHttpBack.err_user_code_send_fail (errCode 40020, message '验证码发送失败'). Thrown by LoginService.postPhoneRegisterCode (login.service.ts:40) when NODE_ENV=production and alicloudSmsService.sendLoginSms returns a falsy result, meaning AliCloud SMS failed to send the code. In non-production the SMS call result is not enforced (the code is returned directly).

Solutions

  1. Check AliCloud SMS credentials and configuration (access key, signature, template id) in the production env
  2. Inspect the AliCloud SMS API response/logs for the reject reason (throttle, invalid phone, blacklisted)
  3. Verify the SMS account has balance and the template/signature are approved
  4. Only set NODE_ENV=production after SMS is verified working, since enforcement only happens in production

Example fix

// env
// before
ALICLOUD_SMS_TEMPLATE=unset
// after
ALICLOUD_ACCESS_KEY=xxx
ALICLOUD_SMS_SIGN=AiToEarn
ALICLOUD_SMS_TEMPLATE=SMS_123456789
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: verify SMS config is reachable before user flows
const cfgOk = !!(process.env.ALICLOUD_ACCESS_KEY && process.env.ALICLOUD_SMS_SIGN && process.env.ALICLOUD_SMS_TEMPLATE)
if (!cfgOk) throw new Error('AliCloud SMS configuration missing')

Type guard

function isSmsConfigured(cfg: Partial<SmsConfig>): cfg is SmsConfig {
  return Boolean(cfg.accessKey && cfg.sign && cfg.templateCode)
}

Try / catch

try {
  await api.postPhoneRegisterCode(phone)
} catch (e) {
  if (e?.errCode === '40020') {
    // log SMS failure details, surface a retry-with-backoff message to the user
  } else throw e
}

Prevention

When it happens

Trigger: Requesting a register SMS code in a production deployment where alicloudSmsService.sendLoginSms(phone, code) resolves to null/false — SMS provider rejects or fails the send.

Common situations: Missing or invalid AliCloud SMS credentials (accessKeyId/secret, sign name, template code); phone number rejected (invalid format, blacklist, throttling by AliCloud); SMS account balance exhausted or template not approved for the region.

Related errors


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

Appendix: source

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

  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
   */
  async postPhoneLoginCode(phone: string) {
    const cacheKey = `${LoginTypeCacheKey.Code}:${phone}`;
    let code = await this.redisService.get(cacheKey);
    if (code) throw new AppHttpException(ErrHttpBack.err_user_code_had);

    code = getRandomString(6, true);

View on GitHub (pinned to d3aa8bea5b)