bytedance/deer-flow · error · HTTPException

The identity provider did not provide an email address.

Error message

The identity provider did not provide an email address.

What it means

HTTP 403 raised when the OIDC identity carries no email address at all. DeerFlow uses email as the account key for provisioning (lookup, domain allow-list, role resolution), so an email-less identity cannot proceed. This is distinct from error 80: the email is absent, not merely unverified.

Source

Thrown at backend/app/gateway/auth/user_provisioning.py:51

    3. Block if a local account already owns the email (never auto-link)
    4. Auto-create if enabled

    Returns a dict with ``user`` (the User model instance) and ``created`` (bool).
    """
    # 1. Existing OAuth link
    existing = await local_provider.get_user_by_oauth(provider_id, identity.subject)
    if existing:
        return {"user": existing, "created": False}

    # 2. Verified email requirement
    if provider_config.require_verified_email and not identity.email_verified:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=("Your email could not be verified by the identity provider. Please contact your administrator."),
        )

    if not identity.email:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="The identity provider did not provide an email address.",
        )

    email = identity.email.lower()

    # 3. Domain restriction
    if provider_config.allowed_email_domains:
        domain = email.rsplit("@", 1)[-1]
        if domain not in {d.lower().lstrip("@") for d in provider_config.allowed_email_domains}:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Your email domain is not allowed. Please use an approved email address.",
            )

    # 4. Block if a local account already owns this email. We never auto-link an
    # SSO identity onto a pre-existing local account, since that would let an SSO
    # login take over a password account that happens to share the email.

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Add the email (and profile) scope to the provider's scopes list in config.yaml so the IdP returns the email claim
  2. Fix the provider's claim mapping so the email attribute is populated for this user
  3. Ensure the user actually has an email set at the identity provider

Example fix

# config.yaml (provider entry)
# before
scopes: [openid]
# after
scopes: [openid, profile, email]
Defensive patterns

Strategy: validation

Validate before calling

if not identity.email:
    return RedirectResponse("/login?error=no_email_claim")
# only then call provisioning

Try / catch

try:
    await provision_oauth_user(provider_id, identity, provider_config)
except HTTPException as e:
    if e.status_code == 403 and "did not provide an email" in e.detail:
        return redirect_to_login("no_email_claim")
    raise

Prevention

When it happens

Trigger: SSO login where the ID token/userinfo has no email claim, or the provider's claim mapping returns an empty value. Only reached when require_verified_email is satisfied (false or verified) and no existing OAuth link exists.

Common situations: OIDC scope list missing 'email'; service-account or CI principals that have no mailbox; custom claim mappers that renamed the email attribute; GitHub-style providers where the primary email is private.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/6b28596e637e0572. Report an issue: GitHub.