theonedev/onedev · error · BadRequestException

Should only convert normal users to service accounts

Error message

Should only convert normal users to service accounts

What it means

The convert-to-service-account endpoint only accepts normal (ordinary) user ids. If userId <= User.ROOT_ID — i.e. a reserved/system account such as root — the request is rejected with this BadRequestException. Service accounts and the root account cannot be converted to service accounts again via the API.

Source

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

			throw new BadRequestException("Should only enable normal users");
		var user = userService.load(userId);
		userService.enable(user);

		auditService.audit(null, "enabled account \"" + user.getName() + "\" via RESTful API", null, null);

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

	@Api(order=1980, description="Convert to service account")
	@Path("/{userId}/convert-to-service-account")
    @POST
    public Response convertToServiceAccount(@PathParam("userId") Long userId) {
		if (!subscriptionService.isSubscriptionActive())
			throw new NotAcceptableException("This operation requires active subscription");
		if (!SecurityUtils.isAdministrator()) 
			throw new UnauthorizedException();
		if (userId <= User.ROOT_ID)		
			throw new BadRequestException("Should only convert normal users to service accounts");
		var user = userService.load(userId);
		userService.convertToServiceAccount(user);

		auditService.audit(null, "converted user \"" + user.getName() + "\" to service account via RESTful API", null, null);

		return Response.ok().build();
    }
	
	@Api(order=2000)
	@Path("/{userId}/password")
    @POST
    public Response setPassword(@PathParam("userId") Long userId, @Password(checkPolicy=true) @NotEmpty String password) {
    	User user = userService.load(userId);
		if (user.isDisabled()) {
			throw new ExplicitException("Cannot set password for disabled account");
		} else if (user.getType() != ORDINARY) {
			throw new ExplicitException("Cannot set password for service or AI account");
		} if (SecurityUtils.isAdministrator()) {

View on GitHub (pinned to d44925c47c)

Solutions

  1. Check userId > User.ROOT_ID (and that the user type is ORDINARY) before calling the endpoint
  2. Fetch the user list and skip system accounts (root) when scripting conversions
  3. Convert privileged/system accounts manually if ever needed, not through this endpoint

Example fix

// before
await rest.post(`/users/${user.id}/convert-to-service-account`); // root id -> 400
// after
if (user.id > 1 && user.type === 'ORDINARY') {
  await rest.post(`/users/${user.id}/convert-to-service-account`);
}
Defensive patterns

Strategy: validation

Validate before calling

if (typeof userId !== 'number' || userId <= 1) throw new Error('Only normal users (id > User.ROOT_ID) can be converted to service accounts');

Type guard

function isConvertibleUser(user) { return user.id > 1 && user.type === 'ORDINARY'; }

Try / catch

try { await rest.post(`/users/${id}/convert-to-service-account`); } catch (e) { if (e.status === 400 && /normal users/.test(e.message)) { /* skip reserved account */ } else throw e; }

Prevention

When it happens

Trigger: Calling POST /rest/v1/users/{userId}/convert-to-service-account with a userId that is the built-in root account or any reserved id <= User.ROOT_ID.

Common situations: Bulk scripts iterating all users including root; assuming ids start at 1/0 for real users; passing a wrong variable as userId.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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