{"record":{"id":"db70ae1e3463956c","repo":"bytedance/deer-flow","slug":"an-account-with-this-email-already-exists-contact","errorCode":null,"errorMessage":"An account with this email already exists. Contact your administrator to link it to your SSO account.","messagePattern":"An account with this email already exists\\. Contact your administrator to link it to your SSO account\\.","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"error","filePath":"backend/app/gateway/auth/user_provisioning.py","lineNumber":73,"sourceCode":"\n    email = identity.email.lower()\n\n    # 3. Domain restriction\n    if provider_config.allowed_email_domains:\n        domain = email.rsplit(\"@\", 1)[-1]\n        if domain not in {d.lower().lstrip(\"@\") for d in provider_config.allowed_email_domains}:\n            raise HTTPException(\n                status_code=status.HTTP_403_FORBIDDEN,\n                detail=\"Your email domain is not allowed. Please use an approved email address.\",\n            )\n\n    # 4. Block if a local account already owns this email. We never auto-link an\n    # SSO identity onto a pre-existing local account, since that would let an SSO\n    # login take over a password account that happens to share the email.\n    local_user = await local_provider.get_user_by_email(email)\n\n    if local_user:\n        raise HTTPException(\n            status_code=status.HTTP_409_CONFLICT,\n            detail=(\"An account with this email already exists. Contact your administrator to link it to your SSO account.\"),\n        )\n\n    # 5. Auto-create\n    if not provider_config.auto_create_users:\n        raise HTTPException(\n            status_code=status.HTTP_403_FORBIDDEN,\n            detail=\"Automatic account creation is disabled. Contact your administrator.\",\n        )\n\n    role = _resolve_role(email, provider_config.admin_emails)\n    try:\n        user = await local_provider.create_oauth_user(\n            email=email,\n            oauth_provider=provider_id,\n            oauth_id=identity.subject,\n            system_role=role,","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/auth/user_provisioning.py#L55-L91","documentation":"HTTP 409 raised when a local (password) account already owns the email and no OAuth link exists. DeerFlow deliberately never auto-links an SSO identity onto a pre-existing local account, because whoever controls the IdP's email namespace could otherwise take over a password account that happens to share the address. The fix requires an administrator to link them out-of-band.","triggerScenarios":"First SSO login by a user who previously registered a local password account with the same email (case-insensitively matched via get_user_by_email). Reached after the verified-email, email-present, and domain checks pass.","commonSituations":"Enabling SSO on an installation that already has password users; a user signing in with SSO before their admin-performed link; email reused across local and IdP accounts.","solutions":["Have an administrator link the SSO identity to the existing local account (out-of-band admin action, e.g. admin UI/CLI link operation)","If the local account is stale, remove or rename it so provisioning can auto-create","Log in with the local (password) credentials instead"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"local_user = await local_provider.get_user_by_email(identity.email.lower())\nlinked = await local_provider.get_user_by_oauth(provider_id, identity.subject)\nif local_user and not linked:\n    return RedirectResponse(\"/login?error=account_exists_contact_admin\")","typeGuard":null,"tryCatchPattern":"try:\n    await provision_oauth_user(provider_id, identity, provider_config)\nexcept HTTPException as e:\n    if e.status_code == 409 and \"already exists\" in e.detail:\n        return page(\"contact-admin-to-link\")  # never auto-link in code either\n    raise","preventionTips":["Before enabling SSO, export the local user list and pre-link known SSO users","Never work around this by auto-linking in a custom callback — the 409 is an anti-takeover guard","Document the admin linking procedure for help-desk staff"],"tags":["auth","oidc","sso","http-409","account-takeover-guard","user-provisioning"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}