langgenius/dify · warning · EmailSendIpLimitError

email_send_ip_limit

email_send_ip_limit

Error message

Too many emails have been sent from this IP address recently. Please try again later.

What it means

Raised by POST /console/api/email-code-login (HTTP 429, code email_send_ip_limit) when AccountService.is_email_send_ip_limit(ip) returns true. The IP is rate-limited in three tiers: per-minute count (EMAIL_SEND_IP_LIMIT_PER_MINUTE), a 10-minute first-strike window, and a 1-hour freeze key set once the hourly strike is exceeded.

Source

Thrown at api/controllers/console/auth/login.py:255

            language=language,
            is_allow_register=FeatureService.get_system_features().is_allow_register,
        )

        return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json")


@console_ns.route("/email-code-login")
class EmailCodeLoginSendEmailApi(Resource):
    @setup_required
    @console_ns.expect(console_ns.models[EmailPayload.__name__])
    @console_ns.response(200, "Success", console_ns.models[SimpleResultDataResponse.__name__])
    @model_validate(EmailPayload)
    def post(self, req_data: EmailPayload):
        normalized_email = req_data.email.lower()

        ip_address = extract_remote_ip(request)
        if AccountService.is_email_send_ip_limit(ip_address):
            raise EmailSendIpLimitError()

        if req_data.language is not None and req_data.language == "zh-Hans":
            language = "zh-Hans"
        else:
            language = "en-US"
        try:
            account = _get_account_with_case_fallback(req_data.email)
        except AccountRegisterError:
            raise AccountInFreezeError()

        if account is None:
            if FeatureService.get_system_features().is_allow_register:
                token = AccountService.send_email_code_login_email(email=normalized_email, language=language)
            else:
                raise AccountNotFound()
        else:
            token = AccountService.send_email_code_login_email(account=account, language=language)

View on GitHub (pinned to ef8544b173)

Solutions

  1. Wait up to 1 hour for the email_send_ip_limit_freeze:<ip> Redis key to expire, then retry.
  2. Switch to a different network/IP or use password login instead of email-code login.
  3. An operator can raise EMAIL_SEND_IP_LIMIT_PER_MINUTE or clear the freeze key: DEL email_send_ip_limit_freeze:<ip>.
  4. For legitimate shared-IP deployments, tune the limit in dify_config to match expected concurrent users.

Example fix

# before
ip_address = extract_remote_ip(request)
if AccountService.is_email_send_ip_limit(ip_address):
    raise EmailSendIpLimitError()
# after - return retry-after hint based on freeze TTL
if AccountService.is_email_send_ip_limit(ip_address):
    ttl = redis_client.ttl(f'email_send_ip_limit_freeze:{ip_address}')
    raise EmailSendIpLimitError(description=f'IP rate limited. Retry in {ttl}s')
Defensive patterns

Strategy: retry

Validate before calling

# Client-side: track last request time per IP/UA and throttle locally
if time.time() - last_send_ts < MIN_INTERVAL:
    show_message('Please wait before requesting another code.')
    return
submit_email_code_request(email)

Type guard

null

Try / catch

from controllers.console.error import EmailSendIpLimitError
try:
    request_code(email)
except EmailSendIpLimitError:
    schedule_retry(after_seconds=3600)  # freeze window is 1 hour
    suggest_password_login()

Prevention

When it happens

Trigger: POST /console/api/email-code-login from an IP that exceeded EMAIL_SEND_IP_LIMIT_PER_MINUTE in the current minute and has already triggered the hourly strike counter, or from an IP whose email_send_ip_limit_freeze:<ip> key is still set (1-hour freeze).

Common situations: Shared office NAT IP sending many code-login emails; automated tests hammering the endpoint from one IP; a script abusing the email-code-login send; users behind a corporate proxy pooled on a single egress IP.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/53dc67ab4e135b8e. Report an issue: GitHub.