getredash/redash · error
Bad email address.
Error message
Bad email address.
What it means
Raised by require_allowed_email in redash/handlers/users.py when the domain part of the email (normalized: lowercased, trailing dot stripped) is in the internal blacklist or in the settings.BLOCKED_DOMAINS configuration. This is a signup/invite spam guard.
Source
Thrown at redash/handlers/users.py:64
def invite_user(org, inviter, user, send_email=True):
d = user.to_dict()
invite_url = invite_link_for_user(user)
if settings.email_server_is_configured() and send_email:
send_invite_email(inviter, user, invite_url, org)
else:
d["invite_link"] = invite_url
return d
def require_allowed_email(email):
# `example.com` and `example.com.` are equal - last dot stands for DNS root but usually is omitted
_, domain = email.lower().rstrip(".").split("@", 1)
if domain in blacklist or domain in settings.BLOCKED_DOMAINS:
abort(400, message="Bad email address.")
class UserListResource(BaseResource):
decorators = BaseResource.decorators + [limiter.limit("200/day;50/hour", methods=["POST"])]
def get_users(self, disabled, pending, search_term):
if disabled:
users = models.User.all_disabled(self.current_org)
else:
users = models.User.all(self.current_org)
if pending is not None:
users = models.User.pending(users, pending)
if search_term:
users = models.User.search(users, search_term)
self.record_event(
{View on GitHub (pinned to ca79fe988d)
Solutions
- Use an email at an allowed (non-blocked) domain.
- If you administer the instance, review/remove the domain from settings.BLOCKED_DOMAINS.
- Normalize the address (lowercase, no trailing dot) before checking against your own list.
Example fix
# before
client.post('/api/users', json={'name': 'X', 'email': 'user@mailinator.com'})
# after
client.post('/api/users', json={'name': 'X', 'email': 'user@corp.com'}) Defensive patterns
Strategy: validation
Validate before calling
domain = email.lower().rstrip('.').split('@', 1)[1]
if domain in blocked_domains:
raise ValueError('email domain blocked') Type guard
def is_allowed_email(email: str, blocked: set) -> bool:
try:
_, domain = email.lower().rstrip('.').split('@', 1)
except ValueError:
return False
return domain not in blocked Prevention
- Test invites with real corporate addresses, not disposable mail.
- Keep a copy of REDASH_BLOCKED_DOMAINS visible to whoever writes invite scripts.
When it happens
Trigger: Inviting a user (POST /api/users, or the invite flow) with an email at a blocked domain, e.g. mailinator.com or any domain listed in REDASH_BLOCKED_DOMAINS.
Common situations: Disposable-email blocking configured via env var; corporate installs blocking personal-mail domains; testers using throwaway addresses that land on the default blacklist.
Related errors
- Email already taken.
- Must provide current password to update password.
- Group id {} is invalid.
- Missing `rows` field in `result` dict.
- Missing `columns` field in `result` dict.
AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28).
Data as JSON: /api/errors/14f2a6f4dfde50dc.
Report an issue: GitHub.