{"record":{"id":"82f4663ee7664e8d","repo":"iflytek/astron-agent","slug":"8102-workflow-version-reduction-failed","errorCode":"8102","errorMessage":"workflow.version.reduction.failed","messagePattern":"workflow\\.version\\.reduction\\.failed","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/VersionService.java","lineNumber":535,"sourceCode":"                            .eq(WorkflowVersion::getId, createDto.getId())\n                            .eq(WorkflowVersion::getFlowId, createDto.getFlowId())\n                            .eq(WorkflowVersion::getDeleted, false)\n                            .last(\"limit 1\"));\n            if (workflowVersion == null) {\n                throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_NOT_FOUND);\n            }\n            String data = workflowVersion.getData();\n            // Update workflow table protocol data\n            updateFlowIdWorkflow(createDto.getFlowId(), data);\n\n            LambdaUpdateWrapper<WorkflowVersion> updateWrapper1 = new LambdaUpdateWrapper<>();\n            // Update flowId corresponding records, set isVersion to 2\n            updateWrapper1.eq(WorkflowVersion::getFlowId, createDto.getFlowId())\n                    .eq(WorkflowVersion::getDeleted, false)\n                    .set(WorkflowVersion::getIsVersion, 2);\n            // Execute update\n            if (workflowVersionMapper.update(null, updateWrapper1) < 1) {\n                throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);\n            }\n\n\n            LambdaUpdateWrapper<WorkflowVersion> updateWrapper2 = new LambdaUpdateWrapper<>();\n            // Update id corresponding records, set isVersion to 1\n            updateWrapper2\n                    .eq(WorkflowVersion::getId, createDto.getId())\n                    .eq(WorkflowVersion::getFlowId, createDto.getFlowId())\n                    .eq(WorkflowVersion::getDeleted, false)\n                    .set(WorkflowVersion::getIsVersion, 1);\n            // Execute update\n            if (workflowVersionMapper.update(null, updateWrapper2) != 1) {\n                throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);\n            }\n\n            return ApiResult.success(new JSONObject());\n        } catch (BusinessException e) {\n            throw e;","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/VersionService.java#L517-L553","documentation":"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.","triggerScenarios":"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.","commonSituations":"Concurrent logicDelete removing all versions between the select and update; data race between two restore calls; flow whose versions were purged externally.","solutions":["Check that at least one live version row exists for the flowId before restoring","Retry the restore — a transient race may have resolved; re-fetch state first","Serialize restore/delete operations per flowId (lock or optimistic check) to avoid concurrent mutation","Inspect workflow_version rows (is_version, deleted) for the flow to confirm DB state before retrying"],"exampleFix":"// before\nif (workflowVersionMapper.update(null, updateWrapper1) < 1) {\n    throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);\n}\n// after\nint updated = workflowVersionMapper.update(null, updateWrapper1);\nif (updated < 1) {\n    log.warn(\"restore: no live versions to demote for flowId={}\", createDto.getFlowId());\n    throw new BusinessException(ResponseEnum.WORKFLOW_VERSION_REDUCTION_FAILED);\n}","handlingStrategy":"retry","validationCode":"Long liveVersions = workflowVersionMapper.selectCount(new LambdaQueryWrapper<WorkflowVersion>()\n    .eq(WorkflowVersion::getFlowId, flowId).eq(WorkflowVersion::getDeleted, false));\nif (liveVersions == 0) { /* nothing to demote — restore precondition broken */ }","typeGuard":null,"tryCatchPattern":"try {\n    return svc.restore(dto);\n} catch (BusinessException e) {\n    if (e.getCode() == 8102) { recheckFlowState(); return retryOnceOrReport(dto); }\n    throw e;\n}","preventionTips":["Serialize restore and logicDelete per flowId (distributed lock or DB row lock)","Run the whole restore in one transaction to avoid partial state","Verify live versions exist before starting restore","Avoid concurrent restore of the same flow from multiple sessions"],"tags":["java","workflow","concurrency"],"backgroundTag":"invalid-state-transition","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}