apache/dolphinscheduler · error · ServiceException

DELETE_WORKFLOW_DEFINE_BY_CODE_ERROR

DELETE_WORKFLOW_DEFINE_BY_CODE_ERROR

Error message

DELETE_WORKFLOW_DEFINE_BY_CODE_ERROR: delete workflow definition by code error

What it means

Thrown when the actual DELETE of a workflow definition version from the log tables affected zero rows — either workflowDefinitionLogMapper.deleteByWorkflowDefinitionCodeAndVersion or workflowTaskRelationLogMapper.deleteByCode returned 0. It indicates the version rows expected to exist in the history tables are missing, so the delete is treated as failed rather than silently succeeding.

Source

Thrown at dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowDefinitionServiceImpl.java:1752

            throw new ServiceException(Status.WORKFLOW_DEFINITION_NOT_EXIST, code);
        }
        if (workflowDefinition.getVersion() == version) {
            log.warn("This version: {} of workflow: {} is the main version cannot delete by version", code, version);
            throw new ServiceException(Status.MAIN_TABLE_USING_VERSION);
        }
        // check whether there exist running workflow instance under the workflow definition
        List<WorkflowInstanceSummaryDto> workflowInstances = workflowInstanceService.queryByWorkflowCodeVersionStatus(
                code,
                version,
                WorkflowExecutionStatus.NOT_TERMINAL_STATES);
        if (CollectionUtils.isNotEmpty(workflowInstances)) {
            throw new ServiceException(Status.DELETE_WORKFLOW_DEFINITION_EXECUTING_FAIL, workflowInstances.size());
        }

        int deleteLog = workflowDefinitionLogMapper.deleteByWorkflowDefinitionCodeAndVersion(code, version);
        int deleteRelationLog = workflowTaskRelationLogMapper.deleteByCode(code, version);
        if (deleteLog == 0 || deleteRelationLog == 0) {
            throw new ServiceException(Status.DELETE_WORKFLOW_DEFINE_BY_CODE_ERROR);
        }
        log.info("Delete version: {} of workflow: {}, projectCode: {}", version, code, projectCode);

    }

    @Transactional
    @Override
    public void onlineWorkflowDefinition(User loginUser, Long projectCode, Long workflowDefinitionCode) {
        projectService.checkHasProjectWritePermissionThrowException(loginUser, projectCode);

        WorkflowDefinition workflowDefinition = workflowDefinitionDao.queryByCode(workflowDefinitionCode)
                .orElseThrow(() -> new ServiceException(Status.WORKFLOW_DEFINITION_NOT_EXIST, workflowDefinitionCode));
        if (projectCode != workflowDefinition.getProjectCode()) {
            throw new ServiceException(Status.WORKFLOW_DEFINITION_NOT_EXIST, workflowDefinitionCode);
        }

        if (ReleaseState.ONLINE.equals(workflowDefinition.getReleaseState())) {
            // do nothing if the workflow is already online

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Verify the (code, version) rows exist in t_ds_workflow_definition_log and t_ds_workflow_task_relation_log before deleting.
  2. Make cleanup scripts idempotent: treat 'already gone' as success by checking existence first.
  3. Serialize cleanup jobs (lock or leader election) to avoid concurrent double-deletes.
  4. If rows are genuinely missing due to data corruption, re-import or repair the definition history.

Example fix

// before: unconditional delete, throws if 0 rows
workflowDefinitionLogMapper.deleteByWorkflowDefinitionCodeAndVersion(code, version);
// after: check existence first
if (workflowDefinitionLogMapper.queryByWorkflowDefinitionCodeAndVersion(code, version) != null) {
    workflowDefinitionLogMapper.deleteByWorkflowDefinitionCodeAndVersion(code, version);
}
Defensive patterns

Strategy: validation

Validate before calling

boolean versionExists =
    workflowDefinitionLogMapper.queryByWorkflowDefinitionCodeAndVersion(code, version) != null
    && !workflowTaskRelationLogMapper.queryByCode(code, version).isEmpty();
if (!versionExists) {
    return; // treat as already deleted (idempotent)
}

Type guard

boolean rowsPresent(int deleteLog, int deleteRelationLog) {
    return deleteLog > 0 && deleteRelationLog > 0;
}

Try / catch

try {
    service.deleteWorkflowDefinitionVersion(user, projectCode, code, version);
} catch (ServiceException e) {
    if (e.getCode() == Status.DELETE_WORKFLOW_DEFINE_BY_CODE_ERROR.getCode()) {
        // rows already gone or partial delete; verify state and continue idempotently
    } else throw e;
}

Prevention

When it happens

Trigger: deleteWorkflowDefinitionVersion on a (code, version) pair absent from t_ds_workflow_definition_log or t_ds_workflow_task_relation_log — e.g., the version was already deleted, or was never logged due to an interrupted save.

Common situations: Double-delete races where two cleanup scripts target the same version concurrently; databases restored from partial backups missing log rows; manually purged history tables.

Related errors


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