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

Setting ownEstimatedTime requires the acting subject to have schedule-issues permission on the project. createIssue throws UnauthorizedException when SecurityUtils.canScheduleIssues(subject, project) is false.

Source

Thrown at server-core/src/main/java/io/onedev/server/ai/IssueHelper.java:291

        var title = (String) data.remove("title");
        if (title == null)
            throw new ExplicitException("Title is required");
        issue.setTitle(title);
        var description = (String) data.remove("description");
        issue.setDescription(description);
        var confidential = (Boolean) data.remove("confidential");
        if (confidential != null)
            issue.setConfidential(confidential);

        Integer ownEstimatedTime = (Integer) data.remove("ownEstimatedTime");
        if (ownEstimatedTime != null) {
            var subscriptionService = OneDev.getInstance(SubscriptionService.class);
            if (!subscriptionService.isSubscriptionActive())
                throw new ExplicitException("An active subscription is required for this feature");
            if (!project.isTimeTracking())
                throw new ExplicitException("Time tracking needs to be enabled for the project");
            if (!SecurityUtils.canScheduleIssues(subject, project))
                throw new UnauthorizedException("Issue schedule permission required to set own estimated time");
            issue.setOwnEstimatedTime(ownEstimatedTime * 60);
        }

        List<String> iterationNames = (List<String>) data.remove("iterations");
        if (iterationNames != null) {
            if (!SecurityUtils.canScheduleIssues(subject, project))
                throw new UnauthorizedException("Issue schedule permission required to set iterations");
            var iterationService = OneDev.getInstance(IterationService.class);
            for (var iterationName : iterationNames) {
                var iteration = iterationService.findInHierarchy(project, iterationName);
                if (iteration == null)
                    throw new ExplicitException("Iteration '" + iterationName + "' not found");
                IssueSchedule schedule = new IssueSchedule();
                schedule.setIssue(issue);
                schedule.setIteration(iteration);
                issue.getSchedules().add(schedule);
            }
        }

View on GitHub (pinned to d44925c47c)

Solutions

  1. Grant the user's role the 'Schedule issues' permission on the project (Project > Access/Authorization)
  2. Use an account with schedule permission for the automation
  3. Remove "ownEstimatedTime" from the payload when the actor lacks the permission

Example fix

// before: low-privilege token sets estimated time -> UnauthorizedException
data.put("ownEstimatedTime", 2);
// after: check first
if (SecurityUtils.canScheduleIssues(subject, project)) data.put("ownEstimatedTime", 2);
Defensive patterns

Strategy: validation

Validate before calling

if (data.containsKey("ownEstimatedTime")
        && !SecurityUtils.canScheduleIssues(subject, project))
    throw new SecurityException("Actor lacks schedule-issues permission");

Try / catch

try {
    issueHelper.createIssue(project, subject, data);
} catch (UnauthorizedException e) {
    if (e.getMessage().contains("schedule permission")) {
        // rerun as privileged account or drop the field
    }
}

Prevention

When it happens

Trigger: A user (or AI acting on their behalf) without issue-schedule permission submits an "ownEstimatedTime" value in createIssue.

Common situations: Service accounts or restricted roles used in automation; AI tools executing with the end user's permissions; permission role changes removed 'Schedule issues' from the user's role.

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/8994c2b6672eb21f. Report an issue: GitHub.