getredash/redash · error

Email already taken.

Error message

Email already taken.

What it means

Raised by UserListResource.post in redash/handlers/users.py when models.db.session.commit() raises an IntegrityError whose message contains 'email' — the org+email unique constraint was violated, so a user with that email already exists in the organization.

Source

Thrown at redash/handlers/users.py:152

        if "@" not in req["email"]:
            abort(400, message="Bad email address.")
        require_allowed_email(req["email"])

        user = models.User(
            org=self.current_org,
            name=req["name"],
            email=req["email"],
            is_invitation_pending=True,
            group_ids=[self.current_org.default_group.id],
        )

        try:
            models.db.session.add(user)
            models.db.session.commit()
        except IntegrityError as e:
            if "email" in str(e):
                abort(400, message="Email already taken.")
            abort(500)

        self.record_event({"action": "create", "object_id": user.id, "object_type": "user"})

        should_send_invitation = "no_invite" not in request.args
        return invite_user(self.current_org, self.current_user, user, send_email=should_send_invitation)


class UserInviteResource(BaseResource):
    @require_admin
    def post(self, user_id):
        user = models.User.get_by_id_and_org(user_id, self.current_org)
        return invite_user(self.current_org, self.current_user, user)


class UserResetPasswordResource(BaseResource):
    @require_admin
    def post(self, user_id):

View on GitHub (pinned to ca79fe988d)

Solutions

  1. Check for an existing user first (GET /api/users?email=...) and reuse it instead of re-inviting.
  2. Make invite scripts idempotent: on this 400, look up and return the existing user.
  3. De-duplicate your invitation list before submitting.

Example fix

# before
client.post('/api/users', json={'name': 'X', 'email': email})

# after
existing = client.get('/api/users', params={'email': email})['results']
if not existing:
    client.post('/api/users', json={'name': 'X', 'email': email})
Defensive patterns

Strategy: validation

Validate before calling

existing = client.get('/api/users', params={'email': email})['results']
if existing:
    return existing[0]

Try / catch

try:
    client.post('/api/users', json=payload)
except HTTPError as e:
    if e.response.status_code == 400 and 'already taken' in e.response.text:
        return get_user_by_email(email)
    raise

Prevention

When it happens

Trigger: POST /api/users inviting an email that is already registered in the same org (active or previously invited).

Common situations: Re-running invite scripts that are not idempotent; inviting someone who already signed up via SSO; retrying after a timeout duplicates the invite.

Related errors


AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28). Data as JSON: /api/errors/f8309d9a2b12f3f3. Report an issue: GitHub.