apache/dolphinscheduler · error · ServiceException

SCHEDULE_ALREADY_EXISTS

SCHEDULE_ALREADY_EXISTS

Error message

SCHEDULE_ALREADY_EXISTS: schedule already exists for workflow definition {workflowDefinitionCode}, scheduleId {scheduleId}

What it means

SchedulerServiceImpl.insertSchedule throws ServiceException(Status.SCHEDULE_ALREADY_EXISTS, workflowDefinitionCode, scheduleId) when a schedule already exists for the given workflow definition code: scheduleDao.queryByWorkflowDefinitionCode returns non-null. DolphinScheduler enforces at most one schedule (timed workflow trigger) per workflow definition, so creating a second is rejected. The message includes the existing schedule's id.

Source

Thrown at dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java:142

                                   String workerGroup,
                                   String tenantCode,
                                   Long environmentCode) {

        Project project = projectDao.queryByCode(projectCode);

        projectService.checkHasProjectWritePermissionThrowException(loginUser, project);

        // check workflow define release state
        WorkflowDefinition workflowDefinition = workflowDefinitionDao.queryByCode(workflowDefinitionCode).orElse(null);
        executorService.checkWorkflowDefinitionValid(projectCode, workflowDefinition, workflowDefinitionCode,
                workflowDefinition.getVersion());

        Schedule scheduleExists =
                scheduleDao.queryByWorkflowDefinitionCode(workflowDefinitionCode);
        if (scheduleExists != null) {
            log.error("Schedule already exist, scheduleId:{}, workflowDefinitionCode:{}", scheduleExists.getId(),
                    workflowDefinitionCode);
            throw new ServiceException(Status.SCHEDULE_ALREADY_EXISTS, workflowDefinitionCode,
                    scheduleExists.getId());
        }

        Schedule scheduleObj = new Schedule();
        Date now = new Date();

        tenantExistValidator.validate(tenantCode);

        scheduleObj.setTenantCode(tenantCode);
        scheduleObj.setProjectName(project.getName());
        scheduleObj.setWorkflowDefinitionCode(workflowDefinitionCode);
        scheduleObj.setWorkflowDefinitionName(workflowDefinition.getName());

        ScheduleParam scheduleParam = JSONUtils.parseObject(schedule, ScheduleParam.class);
        if (DateUtils.differSec(scheduleParam.getStartTime(), scheduleParam.getEndTime()) == 0) {
            log.warn("The start time must not be the same as the end or time can not be null.");
            throw new ServiceException(Status.SCHEDULE_START_TIME_END_TIME_SAME);
        }

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Query existing schedules for the workflow definition first (queryScheduleList) and reuse or update the existing schedule instead of inserting.
  2. If you need different timing, update the existing schedule (updateSchedule) rather than creating a new one.
  3. Deduplicate automation/retry logic: make the create idempotent by catching SCHEDULE_ALREADY_EXISTS and treating it as success.

Example fix

// before: unconditional insert
schedulerService.insertSchedule(loginUser, projectCode, workflowDefinitionCode, schedule);

// after: idempotent guard
if (scheduleDao.queryByWorkflowDefinitionCode(workflowDefinitionCode) == null) {
    schedulerService.insertSchedule(loginUser, projectCode, workflowDefinitionCode, schedule);
} else {
    schedulerService.updateSchedule(loginUser, projectCode, existingId, schedule);
}
Defensive patterns

Strategy: validation

Validate before calling

// check for an existing schedule before inserting
Schedule existing = scheduleDao.queryByWorkflowDefinitionCode(workflowDefinitionCode);
if (existing != null) {
    throw new IllegalStateException("Schedule " + existing.getId() + " already exists for workflow " + workflowDefinitionCode);
}

Try / catch

try {
    schedulerService.insertSchedule(loginUser, projectCode, workflowDefinitionCode, schedule);
} catch (ServiceException e) {
    if (Status.SCHEDULE_ALREADY_EXISTS.getCode() == e.getCode()) {
        // update existing schedule instead
    } else { throw e; }
}

Prevention

When it happens

Trigger: POST /schedules for a workflow definition that already has a timed schedule; double-submitting the create-schedule form; retrying a create call after a timeout when the first request actually succeeded.

Common situations: UI double-click on 'Create Schedule'; automation that creates schedules without checking existing ones; retry logic re-running a request that partially succeeded.

Related errors


AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06). Data as JSON: /api/errors/c2f663c2c18f3a0f. Report an issue: GitHub.