langgenius/dify · error · AccountNotFound
account_not_found
account_not_found
Error message
Account not found.
What it means
Raised by POST /console/api/forgot-password/resets when the reset token is valid and consumed, but no Account row matches the email stored in the token. The controller re-derives the account from reset_data['email'] via AccountService.get_account_by_email_with_case_fallback and, if it returns None, throws AccountNotFound (HTTP 400, code account_not_found). This indicates the account was deleted or its email changed between sending the reset email and submitting the new password.
Source
Thrown at api/controllers/console/auth/forgot_password.py:189
if reset_data.get("phase", "") != "reset":
raise InvalidTokenError()
# Revoke token to prevent reuse
AccountService.revoke_reset_password_token(req_data.token)
# Generate secure salt and hash password
salt = secrets.token_bytes(16)
password_hashed = hash_password(req_data.new_password, salt)
email = reset_data.get("email", "")
account = AccountService.get_account_by_email_with_case_fallback(email, session=db.session())
if account:
account = db.session.merge(account)
self._update_existing_account(account, password_hashed, salt)
db.session.commit()
else:
raise AccountNotFound()
return {"result": "success"}
def _update_existing_account(self, account, password_hashed, salt):
# Update existing account credentials
account.password = base64.b64encode(password_hashed).decode()
account.password_salt = base64.b64encode(salt).decode()
# Create workspace if needed
if (
not TenantService.get_join_tenants(account, session=db.session())
and FeatureService.is_workspace_creation_allowed()
):
TenantService.create_owner_tenant(account, session=db.session())
View on GitHub (pinned to ef8544b173)
Solutions
- Verify the account still exists: query Account by the email the user is attempting to reset, and confirm the account was not deleted or had its email changed.
- Tell the user to request a fresh forgot-password email; the token they hold is bound to a now-absent account.
- If the account was deleted within the 30-day freeze window, the user must wait for the freeze to expire before re-registering; instruct them accordingly.
- Check Redis token store vs. relational DB consistency if this appears in bulk after a migration or backup restore.
Example fix
// This is server-side; no client fix possible for a missing account.
// Guard on the client by surfacing the error code:
// before
fetch('/console/api/forgot-password/resets', {...})
.catch(e => alert('reset failed'))
// after
fetch('/console/api/forgot-password/resets', {...})
.then(r => r.json()).then(body => {
if (body.code === 'account_not_found') {
showInfo('This account no longer exists. Please sign up again or contact your admin.')
}
}) Defensive patterns
Strategy: try-catch
Validate before calling
# Before calling the reset endpoint, confirm the account still exists
# (requires an authenticated admin/owner context; the public endpoint intentionally
# does not leak account existence). For an admin client:
resp = session.get(f'/console/api/workspaces/me/members', params={'email': email})
exists = any(m['email'] == email for m in resp.json().get('data', []))
if not exists:
skip_reset() # do not POST /forgot-password/resets Type guard
null
Try / catch
try:
resp = client.post('/console/api/forgot-password/resets', json=payload)
resp.raise_for_status()
except HTTPError as e:
body = e.response.json()
if body.get('code') == 'account_not_found':
# token email no longer maps to an account; ask user to re-register
prompt_user_to_register()
else:
raise Prevention
- Treat a forgotten-password request as ephemeral; if the user delayed completing it, re-request a fresh token.
- When deleting accounts, ensure any in-flight reset tokens in Redis are also revoked.
- Do not persist reset tokens client-side beyond the immediate session.
When it happens
Trigger: POST /console/api/forgot-password/resets with a valid phase='reset' token whose stored email no longer maps to any Account row. Happens when the user deletes their account after requesting a reset, an admin purges the account, the account email was changed, or the reset token was issued for an email that only ever existed in a different case-sensitivity state that the case-fallback lookup cannot recover.
Common situations: Account deleted during the reset flow window; email changed after requesting reset; testing against a stale DB with token data in Redis but no matching account; multi-instance setups where Redis token data outlives the relational DB row; race condition where a GDPR/CCPA deletion job runs between token issuance and reset.
Related errors
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/c66ad97684499bd7.
Report an issue: GitHub.