{"record":{"id":"5d6157afea8d0f64","repo":"alibaba/spring-cloud-alibaba","slug":"stock-not-enough","errorCode":null,"errorMessage":"stock not enough","messagePattern":"stock not enough","errorType":"exception","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"spring-cloud-alibaba-examples/integrated-example/integrated-order/src/main/java/com/alibaba/cloud/integration/order/service/impl/OrderServiceImpl.java","lineNumber":69,"sourceCode":"\t@Autowired\n\tprivate AccountServiceFeignClient accountService;\n\n\t@Autowired\n\tprivate StorageServiceFeignClient storageService;\n\n\t@Override\n\t@GlobalTransactional\n\tpublic Result<?> createOrder(String userId, String commodityCode, Integer count) {\n\n\t\tlogger.info(\"[createOrder] current XID: {}\", RootContext.getXID());\n\n\t\t// deduct storage\n\t\tStorageDTO storageDTO = new StorageDTO();\n\t\tstorageDTO.setCommodityCode(commodityCode);\n\t\tstorageDTO.setCount(count);\n\t\tInteger storageCode = storageService.reduceStock(storageDTO).getCode();\n\t\tif (storageCode.equals(COMMON_FAILED.getCode())) {\n\t\t\tthrow new BusinessException(\"stock not enough\");\n\t\t}\n\n\t\t// deduct balance\n\t\tint price = count * 2;\n\t\tAccountDTO accountDTO = new AccountDTO();\n\t\taccountDTO.setUserId(userId);\n\t\taccountDTO.setPrice(price);\n\t\tInteger accountCode = accountService.reduceBalance(accountDTO).getCode();\n\t\tif (accountCode.equals(COMMON_FAILED.getCode())) {\n\t\t\tthrow new BusinessException(\"balance not enough\");\n\t\t}\n\n\t\t// save order\n\t\tOrder order = new Order();\n\t\torder.setUserId(userId);\n\t\torder.setCommodityCode(commodityCode);\n\t\torder.setCount(count);\n\t\torder.setMoney(price);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/alibaba/spring-cloud-alibaba/blob/115d5901102009492e05d5ec18c3f79cad4077d0/spring-cloud-alibaba-examples/integrated-example/integrated-order/src/main/java/com/alibaba/cloud/integration/order/service/impl/OrderServiceImpl.java#L51-L87","documentation":"Thrown by OrderServiceImpl.createOrder, which is annotated @GlobalTransactional (a Seata global transaction). After calling the storage service over Feign (storageService.reduceStock), if the returned Result code equals COMMON_FAILED (2003), it throws BusinessException(\"stock not enough\"). Because the method is the global-transaction root, this exception triggers Seata to roll back every branch already enlisted (the stock deduction). It is the orchestrator-level signal that the downstream storage branch rejected the request.","triggerScenarios":"createOrder(userId, commodityCode, count) is called when the storage service cannot satisfy the stock (the storage branch threw BusinessException, which the Feign client / Result wrapper maps to Result.failed with code 2003). The order service sees storageCode == 2003 and aborts the global transaction.","commonSituations":"Storage table seeded with fewer units than the requested count; a prior order exhausted the commodity; concurrent orders competing for the last units of one commodityCode.","solutions":["Check the storage row: SELECT count FROM storage WHERE commodity_code = '<commodityCode>' and ensure it is >= the requested count.","Replenish stock: UPDATE storage SET count = <units> WHERE commodity_code = '<commodityCode>'.","Reduce the requested count in the order so it fits available stock.","Verify the storage service is up and its @Transactional branch is not rolling back for a different reason that still surfaces as COMMON_FAILED."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before createOrder, ensure stock covers the requested count via the storage query endpoint.\nResult<?> stockResult = storageService.getRemainCount(commodityCode);\nInteger remain = (Integer) stockResult.getData();\nif (remain == null || remain < count) {\n    return Result.failed(\"stock not enough\");\n}","typeGuard":null,"tryCatchPattern":"// createOrder is @GlobalTransactional; let BusinessException propagate to roll back branches.\ntry {\n    orderService.createOrder(userId, commodityCode, count);\n} catch (BusinessException e) {\n    // 'stock not enough' -> global transaction already rolled back; return user-facing error\n}","preventionTips":["Pre-check stock via the storage service query endpoint.","Replenish stock in the storage table before load tests.","Treat BusinessException as a clean rollback signal, not a crash."],"tags":["seata","distributed-transaction","orchestration","stock","global-rollback"],"backgroundTag":null,"analyzedSha":"115d5901102009492e05d5ec18c3f79cad4077d0","analyzedAt":"2026-08-14T04:47:13.900Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}