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

  1. Check the storage row: SELECT count FROM storage WHERE commodity_code = '<commodityCode>' and ensure it is >= the requested count.
  2. Replenish stock: UPDATE storage SET count = <units> WHERE commodity_code = '<commodityCode>'.
  3. Reduce the requested count in the order so it fits available stock.
  4. 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

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.