makeplane/plane · warning · AuthenticationException
5100
5100
Error message
EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN
What it means
Raised in MagicCodeProvider.initiate (magic_code.py:92) when the rate limit on issuing magic codes is exceeded for an email. The Redis key 'magic_'+email holds {current_attempt, email, token}. When an existing key has data['current_attempt'] > 2 AND a User with that email exists, code 5100 (SIGN_IN variant) is raised with payload {email}. This guards the issue path, not the verify path.
Source
Thrown at apps/api/plane/authentication/provider/credentials/magic_code.py:92
def initiate(self):
## Generate a random token
token = str(secrets.randbelow(900000) + 100000)
ri = redis_instance()
key = "magic_" + str(self.key)
# Check if the key already exists in python
if ri.exists(key):
data = json.loads(ri.get(key))
current_attempt = data["current_attempt"] + 1
if data["current_attempt"] > 2:
email = str(self.key).replace("magic_", "", 1)
if User.objects.filter(email=email).exists():
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN"],
error_message="EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN",
payload={"email": str(email)},
)
else:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP"],
error_message="EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP",
payload={"email": self.key},
)
value = {
"current_attempt": current_attempt,
"email": str(self.key),
"token": token,
}
expiry = 600
ri.set(key, json.dumps(value), ex=expiry)View on GitHub (pinned to 1c8a60f858)
Solutions
- Wait for the Redis key to expire (TTL 600s) or have the user slow down resend requests.
- Throttle the client-side 'resend' button (e.g., 60s cooldown) to stay under the 3-issue budget.
- If legitimately locked, an admin can delete the 'magic_<email>' Redis key to reset the counter.
Example fix
// before: client auto-resends on every focus -> current_attempt > 2 -> 5100 // after: client enforces a 60s resend cooldown and stops at 3 sends
Defensive patterns
Strategy: validation
Validate before calling
import json
from plane.settings.redis import redis_instance
def can_issue_magic_code(email: str) -> bool:
ri = redis_instance()
key = 'magic_' + str(email)
if not ri.exists(key):
return True
return json.loads(ri.get(key)).get('current_attempt', 0) <= 2 Try / catch
try:
provider.initiate()
except AuthenticationException as e:
if e.error_code == 5100:
tell_user_to_wait_then_retry(email=e.payload.get('email'))
else:
raise Prevention
- Enforce a client-side resend cooldown (>=60s).
- Cap resend attempts to stay within the 3-issue budget per 600s window.
When it happens
Trigger: Calling initiate() repeatedly for the same email: each call increments current_attempt. Once it exceeds 2 (i.e., the 4th+ request within the 600s TTL), the existence check decides SIGN_IN vs SIGN_UP. Existing user -> 5100. Note the off-by-one: the check uses the pre-increment value, so the limit triggers a step later than the counter name implies.
Common situations: User spamming 'resend code', a misbehaving client auto-retrying, or an attacker probing the magic-code flow for a known email.
Related errors
AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12).
Data as JSON: /api/errors/5a7e1392c4d0a105.
Report an issue: GitHub.