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 onlineView on GitHub (pinned to 02eac45a1b)
Solutions
- Verify the (code, version) rows exist in t_ds_workflow_definition_log and t_ds_workflow_task_relation_log before deleting.
- Make cleanup scripts idempotent: treat 'already gone' as success by checking existence first.
- Serialize cleanup jobs (lock or leader election) to avoid concurrent double-deletes.
- 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
- Run only one cleanup job at a time to avoid double-deletes.
- Make delete scripts idempotent by checking row existence first.
- Verify backups/restores include the *_log tables, not just the main table.
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.