theonedev/onedev · error · ExplicitException

Cannot delete primary email address of externally authentica

Error message

Cannot delete primary email address of externally authenticated user

What it means

deleteEmailAddress refuses to remove a user's primary email address when the account has no local password, i.e. it is authenticated externally (SSO/LDAP). The primary address is the account identity for such users, so deleting it would leave the externally-authenticated account without a resolvable email. It throws ExplicitException, which is surfaced as a user-facing message.

Source

Thrown at server-core/src/main/java/io/onedev/server/rest/resource/EmailAddressResource.java:142

			throw new ExplicitException("Unable to send verification email as mail service is not configured");
		if (emailAddress.isVerified())
			throw new ExplicitException("Unable to send verification email as this email address is already verified");
		
		emailAddressService.sendVerificationEmail(emailAddress);
		
		return emailAddressId;
	}
	
	@Api(order=300)
	@Path("/{emailAddressId}")
	@DELETE
	public Response deleteEmailAddress(@PathParam("emailAddressId") Long emailAddressId) {
		var emailAddress = emailAddressService.load(emailAddressId);
		if (!SecurityUtils.isAdministrator() && !emailAddress.getOwner().equals(getAuthUser())) 
			throw new UnauthorizedException();
		
		if (emailAddress.isPrimary() && emailAddress.getOwner().getPassword() == null) {
			throw new ExplicitException("Cannot delete primary email address of "
					+ "externally authenticated user");
		}
		if (emailAddress.getOwner().getEmailAddresses().size() == 1)
			throw new ExplicitException("At least one email address should be present for a user");
		emailAddressService.delete(emailAddress);

		if (!getAuthUser().equals(emailAddress.getOwner())) 
			auditService.audit(null, "deleted email address \"" + emailAddress.getValue() + "\" from account \"" + emailAddress.getOwner().getName() + "\" via RESTful API", null, null);

		return Response.ok().build();
	}
	
}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Delete a non-primary address instead, or add another address first.
  2. For an externally authenticated user, update the email at the identity provider (LDAP/OAuth source) rather than in OneDev.
  3. Convert the user to password (local) authentication if you truly need to change their primary address locally.
  4. As admin, manage the account via the UI which offers clearer guidance for external accounts.
Defensive patterns

Strategy: validation

Validate before calling

// skip primary address of externally authenticated users
if (address.isPrimary() && address.getOwner().getPassword() == null) {
    log.info("Skipping primary address of external user " + address.getOwner().getName());
    return;
}

Try / catch

try { client.deleteEmailAddress(id); }
catch (ExplicitException e) { /* expected for primary/external/last address — treat as skip */ }

Prevention

When it happens

Trigger: DELETE /~api/emailAddresses/{id} where emailAddress.isPrimary() is true and emailAddress.getOwner().getPassword() == null (user authenticates via LDAP/SSO instead of a OneDev password).

Common situations: Users synchronized from LDAP or logging in via OAuth/SSO whose only stable email is the primary one; automation scripts bulk-pruning addresses that hit the primary of an SSO account.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/1af2ed08dd00a973. Report an issue: GitHub.