getredash/redash · error

Not found

Error message

Not found

What it means

Raised by UserResetPasswordResource.post in redash/handlers/users.py when an admin requests a password reset link for a user that exists but is disabled (is_disabled). The 404 deliberately hides disabled accounts rather than revealing their state.

Source

Thrown at redash/handlers/users.py:173

        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):
        user = models.User.get_by_id_and_org(user_id, self.current_org)
        if user.is_disabled:
            abort(404, message="Not found")
        reset_link = send_password_reset_email(user)

        return {"reset_link": reset_link}


class UserRegenerateApiKeyResource(BaseResource):
    def post(self, user_id):
        user = models.User.get_by_id_and_org(user_id, self.current_org)
        if user.is_disabled:
            abort(404, message="Not found")
        if not is_admin_or_owner(user_id):
            abort(403)

        user.regenerate_api_key()
        models.db.session.commit()

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

View on GitHub (pinned to ca79fe988d)

Solutions

  1. Re-enable the user first (POST /api/users/<id> is_disabled=false), then request the reset link.
  2. Verify the user's disabled state via GET /api/users/<id> before calling.
  3. If the user should stay disabled, skip the reset entirely.

Example fix

# before
link = client.post(f'/api/users/{uid}/reset_password')['reset_link']

# after
client.post(f'/api/users/{uid}', json={'is_disabled': False})
link = client.post(f'/api/users/{uid}/reset_password')['reset_link']
Defensive patterns

Strategy: validation

Validate before calling

u = client.get(f'/api/users/{uid}')
if u.get('is_disabled'):
    client.post(f'/api/users/{uid}', json={'is_disabled': False})
link = client.post(f'/api/users/{uid}/reset_password')['reset_link']

Prevention

When it happens

Trigger: POST /api/users/<id>/reset_password where the user record has is_disabled=True (deactivated account).

Common situations: Offboarding flows that disable users while stale admin tooling still tries to reset their password; attempting to reset a password before re-enabling the account.

Related errors


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