theonedev/onedev · error · ExplicitException
At least one email address should be present for a user
Error message
At least one email address should be present for a user
What it means
deleteEmailAddress requires every user to retain at least one email address, so deleting the user's last remaining address throws ExplicitException("At least one email address should be present for a user"). This invariant keeps the account reachable for notifications, commits attribution and login-related flows.
Source
Thrown at server-core/src/main/java/io/onedev/server/rest/resource/EmailAddressResource.java:146
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
- Add a new email address (POST /~api/emailAddresses) before deleting the last one.
- Skip addresses that are the user's last one in your cleanup script (check count > 1 first).
- If the whole account should go away, deactivate/delete the user account instead of its addresses.
Example fix
// before: delete blindly
addresses.forEach(a -> client.deleteEmailAddresses(a.getId()));
// after: keep at least one
if (addresses.size() > 1)
addresses.forEach(a -> client.deleteEmailAddresses(a.getId())); Defensive patterns
Strategy: validation
Validate before calling
// only delete when the user keeps at least one address
var remaining = owner.getEmailAddresses().size();
if (remaining <= 1) throw new IllegalStateException("refusing to delete last email address of " + owner.getName()); Try / catch
try { client.deleteEmailAddress(id); }
catch (ExplicitException e) { log.warn("Cannot remove last email address; add a replacement first"); } Prevention
- Count the user's addresses before each delete in bulk scripts.
- Add the replacement address before removing the old one.
- Treat per-account invariants (min one address, primary constraints) as preconditions in your automation.
When it happens
Trigger: DELETE /~api/emailAddresses/{id} when emailAddress.getOwner().getEmailAddresses().size() == 1, i.e. the target is the user's only email address.
Common situations: Scripts iterating all addresses of a user and deleting each without checking remaining count; cleanup jobs that accidentally remove the sole address of a new 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
- ${e.getMessage()}
- At least one receiver address should be specified
- Subject required to open issue via email
- This address is intended to open issues, however the message
- This email address is already used by another user
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/37c8212ca44ff117.
Report an issue: GitHub.