theonedev/onedev · error · UnauthorizedException

Issue schedule permission required to set own estimated time

Error message

Issue schedule permission required to set own estimated time

What it means

The own-estimated-time endpoint requires the authenticated user to have issue schedule permission in the issue's project. SecurityUtils.canScheduleIssues(project) returning false yields an UnauthorizedException (HTTP 401), thrown after subscription and time-tracking checks.

Source

Thrown at server-core/src/main/java/io/onedev/server/rest/resource/IssueResource.java:411

    	if (!SecurityUtils.canModifyIssue(subject, issue))
			throw new UnauthorizedException();
		issueChangeService.changeConfidential(user, issue, confidential);
		return Response.ok().build();
    }

	@Api(order=1275)
	@Path("/{issueId}/own-estimated-time")
	@POST
	public Response setOwnEstimatedTime(@PathParam("issueId") Long issueId, int minutes) {
		Issue issue = issueService.load(issueId);
		var subject = SecurityUtils.getSubject();
		var user = SecurityUtils.getUser(subject);
		if (!subscriptionService.isSubscriptionActive())
			throw new NotAcceptableException("An active subscription is required for this feature");
		if (!issue.getProject().isTimeTracking())
			throw new NotAcceptableException("Time tracking needs to be enabled for the project");
		if (!SecurityUtils.canScheduleIssues(issue.getProject()))
			throw new UnauthorizedException("Issue schedule permission required to set own estimated time");
		issueChangeService.changeOwnEstimatedTime(user, issue, minutes);
		return Response.ok().build();
	}
	
	@Api(order=1300, description="Schedule issue into specified iterations with list of iteration id")
	@Path("/{issueId}/iterations")
    @POST
    public Response setIterations(@PathParam("issueId") Long issueId, List<Long> iterationIds) {
		Issue issue = issueService.load(issueId);
		var subject = SecurityUtils.getSubject();
		var user = SecurityUtils.getUser(subject);
    	if (!SecurityUtils.canScheduleIssues(subject, issue.getProject()))
			throw new UnauthorizedException("Issue schedule permission required to set iterations");
		
    	Collection<Iteration> iterations = new HashSet<>();
    	for (Long iterationId: iterationIds) {
    		Iteration iteration = iterationService.load(iterationId);
	    	if (!iteration.getProject().isSelfOrAncestorOf(issue.getProject()))

View on GitHub (pinned to d44925c47c)

Solutions

  1. Grant the user's role the 'Schedule issues' permission for the project.
  2. Call the endpoint with credentials of a user who can schedule issues.
  3. Use the UI or a permitted account for estimate updates.

Example fix

// before: 401 Issue schedule permission required to set own estimated time
// after: Project -> Access Management -> role -> enable Schedule Issues, then retry
Defensive patterns

Strategy: validation

Validate before calling

const perms = await getMyProjectPermissions(projectPath);
if (!perms.includes('SCHEDULE_ISSUES'))
  throw new Error('Cannot set own estimated time: schedule issue permission missing');

Type guard

function hasSchedulePermission(subject, project) {
  return subject?.effectivePermissions?.[project.path]?.includes('SCHEDULE_ISSUES') ?? false;
}

Try / catch

try {
  await api.setOwnEstimatedTime(issueId, minutes);
} catch (e) {
  if (e.status === 401 && /Issue schedule permission/.test(e.message))
    throw new Error('Grant Schedule Issues permission to the token owner');
  throw e;
}

Prevention

When it happens

Trigger: POST to /api/issues/{issueId}/own-estimated-time by a user (or token) lacking the schedule issues permission in the issue's project.

Common situations: CI/service accounts with read/report access only; users whose role was stripped of schedule permission; using a personal access token of a restricted user in scripts.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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