theonedev/onedev · error · ExplicitException

Root user cannot be deleted

Error message

Root user cannot be deleted

What it means

OneDev refuses to delete the root (built-in administrator) user via the REST deleteUser endpoint. The root account is a system bootstrap account and must always exist; deleting it would leave the instance potentially unadministrable. The DELETE call throws ExplicitException before userService.delete is invoked.

Source

Thrown at server-core/src/main/java/io/onedev/server/rest/resource/UserResource.java:628

		if (!getAuthUser().equals(user)) {
			var newAuditContent = VersionedXmlDoc.fromBean(sshKey).toXML();
			auditService.audit(null, "added ssh key to account \"" + user.getName() + "\" via RESTful API", null, newAuditContent);
		}

		return sshKey.getId();
	}
	
	@Api(order=2300)
	@Path("/{userId}")
    @DELETE
    public Response deleteUser(@PathParam("userId") Long userId) {
    	if (!SecurityUtils.isAdministrator())
			throw new UnauthorizedException();

    	User user = userService.load(userId);
    	if (user.isRoot())
			throw new ExplicitException("Root user cannot be deleted");
    	else if (user.equals(getAuthUser()))
    		throw new ExplicitException("Cannot delete yourself");
    	else
    		userService.delete(user);

		var oldAuditContent = VersionedXmlDoc.fromBean(getData(user)).toXML();
		auditService.audit(null, "deleted account \"" + user.getName() + "\" via RESTful API", oldAuditContent, null);

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

	public static class UserData implements Serializable {

		private static final long serialVersionUID = 1L;
		
		@Api(order=5, description="ID of the user")
		private Long id;

View on GitHub (pinned to d44925c47c)

Solutions

  1. Exclude the root user from deletion lists (skip users where isRoot is true)
  2. If the root account needs to be locked down, rename it or disable password login instead of deleting
  3. Never iterate 'delete all users' against an OneDev instance without filtering

Example fix

// before
for (long id : userIds) deleteUser(id);
// after
for (long id : userIds) { User u = getUser(id); if (!u.isRoot()) deleteUser(id); }
Defensive patterns

Strategy: validation

Validate before calling

User u = getUser(userId); if (u.isRoot()) throw new SkipException("refusing to delete root");

Type guard

boolean isDeletable(User u) { return u != null && !u.isRoot(); }

Try / catch

try { deleteUser(userId); } catch (ExplicitException e) { log.warn("delete rejected: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Calling DELETE /users/{userId} with the id of the root user (User.isRoot()==true) as an administrator.

Common situations: Bulk user-cleanup scripts that don't exclude the root account; attempting to 'reset' an instance by deleting all users including root.

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/19e48431aaa54f39. Report an issue: GitHub.