iflytek/astron-agent · error · BusinessException

8102

8102

Error message

workflow.version.reduction.failed

What it means

restore throws WORKFLOW_VERSION_REDUCTION_FAILED (code 8102) when the bulk update that marks all versions of the flow as isVersion=2 (non-current) affects fewer than 1 row. Restore requires demarcating old versions before promoting the target one.

Solutions

  1. Check that at least one live version row exists for the flowId before restoring
  2. Retry the restore — a transient race may have resolved; re-fetch state first
  3. Serialize restore/delete operations per flowId (lock or optimistic check) to avoid concurrent mutation
  4. Inspect workflow_version rows (is_version, deleted) for the flow to confirm DB state before retrying

Example fix

// before
if (workflowVersionMapper.update(null, updateWrapper1) < 1) {
    throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);
}
// after
int updated = workflowVersionMapper.update(null, updateWrapper1);
if (updated < 1) {
    log.warn("restore: no live versions to demote for flowId={}", createDto.getFlowId());
    throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);
}
Defensive patterns

Strategy: retry

Validate before calling

Long liveVersions = workflowVersionMapper.selectCount(new LambdaQueryWrapper<WorkflowVersion>()
    .eq(WorkflowVersion::getFlowId, flowId).eq(WorkflowVersion::getDeleted, false));
if (liveVersions == 0) { /* nothing to demote — restore precondition broken */ }

Try / catch

try {
    return svc.restore(dto);
} catch (BusinessException e) {
    if (e.getCode() == 8102) { recheckFlowState(); return retryOnceOrReport(dto); }
    throw e;
}

Prevention

When it happens

Trigger: workflowVersionMapper.update(null, updateWrapper1) returns 0 — no live versions exist for the flowId at that moment (e.g. all deleted concurrently) even though the target version was found moments earlier.

Common situations: Concurrent logicDelete removing all versions between the select and update; data race between two restore calls; flow whose versions were purged externally.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/82f4663ee7664e8d. Report an issue: GitHub.

Appendix: source

Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/VersionService.java:535

                            .eq(WorkflowVersion::getId, createDto.getId())
                            .eq(WorkflowVersion::getFlowId, createDto.getFlowId())
                            .eq(WorkflowVersion::getDeleted, false)
                            .last("limit 1"));
            if (workflowVersion == null) {
                throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_NOT_FOUND);
            }
            String data = workflowVersion.getData();
            // Update workflow table protocol data
            updateFlowIdWorkflow(createDto.getFlowId(), data);

            LambdaUpdateWrapper<WorkflowVersion> updateWrapper1 = new LambdaUpdateWrapper<>();
            // Update flowId corresponding records, set isVersion to 2
            updateWrapper1.eq(WorkflowVersion::getFlowId, createDto.getFlowId())
                    .eq(WorkflowVersion::getDeleted, false)
                    .set(WorkflowVersion::getIsVersion, 2);
            // Execute update
            if (workflowVersionMapper.update(null, updateWrapper1) < 1) {
                throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);
            }


            LambdaUpdateWrapper<WorkflowVersion> updateWrapper2 = new LambdaUpdateWrapper<>();
            // Update id corresponding records, set isVersion to 1
            updateWrapper2
                    .eq(WorkflowVersion::getId, createDto.getId())
                    .eq(WorkflowVersion::getFlowId, createDto.getFlowId())
                    .eq(WorkflowVersion::getDeleted, false)
                    .set(WorkflowVersion::getIsVersion, 1);
            // Execute update
            if (workflowVersionMapper.update(null, updateWrapper2) != 1) {
                throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);
            }

            return ApiResult.success(new JSONObject());
        } catch (BusinessException e) {
            throw e;

View on GitHub (pinned to 5e758547a8)