alibaba/spring-cloud-alibaba · error · BusinessException
stock not enough
Error message
stock not enough
What it means
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.
Source
Thrown at spring-cloud-alibaba-examples/integrated-example/integrated-order/src/main/java/com/alibaba/cloud/integration/order/service/impl/OrderServiceImpl.java:69
@Autowired
private AccountServiceFeignClient accountService;
@Autowired
private StorageServiceFeignClient storageService;
@Override
@GlobalTransactional
public Result<?> createOrder(String userId, String commodityCode, Integer count) {
logger.info("[createOrder] current XID: {}", RootContext.getXID());
// deduct storage
StorageDTO storageDTO = new StorageDTO();
storageDTO.setCommodityCode(commodityCode);
storageDTO.setCount(count);
Integer storageCode = storageService.reduceStock(storageDTO).getCode();
if (storageCode.equals(COMMON_FAILED.getCode())) {
throw new BusinessException("stock not enough");
}
// deduct balance
int price = count * 2;
AccountDTO accountDTO = new AccountDTO();
accountDTO.setUserId(userId);
accountDTO.setPrice(price);
Integer accountCode = accountService.reduceBalance(accountDTO).getCode();
if (accountCode.equals(COMMON_FAILED.getCode())) {
throw new BusinessException("balance not enough");
}
// save order
Order order = new Order();
order.setUserId(userId);
order.setCommodityCode(commodityCode);
order.setCount(count);
order.setMoney(price);View on GitHub (pinned to 115d590110)
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.
Defensive patterns
Strategy: validation
Validate before calling
// Before createOrder, ensure stock covers the requested count via the storage query endpoint.
Result<?> stockResult = storageService.getRemainCount(commodityCode);
Integer remain = (Integer) stockResult.getData();
if (remain == null || remain < count) {
return Result.failed("stock not enough");
} Try / catch
// createOrder is @GlobalTransactional; let BusinessException propagate to roll back branches.
try {
orderService.createOrder(userId, commodityCode, count);
} catch (BusinessException e) {
// 'stock not enough' -> global transaction already rolled back; return user-facing error
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14).
Data as JSON: /api/errors/5d6157afea8d0f64.
Report an issue: GitHub.