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
- Check userId > User.ROOT_ID (and that the user type is ORDINARY) before calling the endpoint
- Fetch the user list and skip system accounts (root) when scripting conversions
- 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
- Exclude reserved/system accounts (root) from conversion scripts
- Convert only accounts whose type is ORDINARY
- Fetch account metadata before mutating it via REST
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
- Should only enable normal users
- Reviewer not found:
- Assignee not found:
- Not authenticated
- Unauthorized
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/c33e348a33bf3189.
Report an issue: GitHub.