chatboxai/chatbox · warning · Error

Failed to send login code

Error message

Failed to send login code

What it means

Thrown by sendCode when sendEmailLoginCode resolves with any value other than the literal 'sent'. The login-code endpoint is expected to acknowledge success with 'sent'; any other payload is treated as a rejection. The throw is caught immediately and converted to a user-facing loginError, so it is a control-flow signal more than a crash.

Source

Thrown at src/renderer/routes/settings/provider/chatbox-ai/-components/useLogin.ts:116

      return false
    }

    try {
      setLoginState('sending_code')
      setLoginError('')
      setCode('')

      const result = await sendEmailLoginCode({
        email: trimmedEmail,
        lang: getLanguagePath(language),
      })

      if (requestEpoch !== requestEpochRef.current) {
        return false
      }

      if (result !== 'sent') {
        throw new Error('Failed to send login code')
      }

      setLoginState('code_sent')
      setHasEnteredCodeStep(true)
      setCountdown(EMAIL_CODE_RESEND_SECONDS)
      return true
    } catch (error: unknown) {
      if (requestEpoch !== requestEpochRef.current) {
        return false
      }

      console.error('Failed to send login code:', error)
      const errorMsg = getReadableErrorMessage(error, t('Failed to send login code'))
      setLoginError(errorMsg)
      setLoginState('error')
      return false
    }
  }, [email, language, t])

View on GitHub (pinned to 81571269ad)

Solutions

  1. Differentiate non-'sent' values: map known backend reasons (rate_limited, invalid_email) to specific UI messages instead of a generic throw.
  2. Confirm the sendEmailLoginCode return contract matches the backend (string 'sent' vs object).
  3. Throttle the send button with the EMAIL_CODE_RESEND_SECONDS countdown before the request fires.
  4. Log the actual returned value (redacted) to identify contract drift.

Example fix

// before
if (result !== 'sent') {
  throw new Error('Failed to send login code')
}
// after
if (result !== 'sent') {
  const reason = typeof result === 'string' ? result : 'unknown'
  throw new Error(`Failed to send login code: ${reason}`)
}
Defensive patterns

Strategy: validation

Validate before calling

const result = await sendEmailLoginCode({ email: trimmedEmail, lang: getLanguagePath(language) })
if (result !== 'sent') {
  // result carries the backend reason; map it before throwing
}

Type guard

type SendCodeResult = 'sent' | 'rate_limited' | 'invalid_email' | 'unknown'
function isSendSuccess(r: unknown): r is 'sent' {
  return r === 'sent'
}

Try / catch

try {
  const result = await sendEmailLoginCode(...)
  if (!isSendSuccess(result)) setLoginError(mapSendCodeResult(result))
} catch (error) {
  setLoginError(getReadableErrorMessage(error, t('Failed to send login code')))
}

Prevention

When it happens

Trigger: sendEmailLoginCode returns a non-'sent' string — backend-reported reasons such as rate limiting, invalid/unknown email, banned account, or an undocumented status. Also fires if the endpoint payload shape changes (e.g. returns { status: 'sent' }) so the strict equality check fails.

Common situations: User clicks 'send code' repeatedly and hits server-side rate limiting, email is malformed in a way the client regex allowed, backend deploy changes the response contract, or the account is flagged.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/8729d8e8cd80a25a. Report an issue: GitHub.