alibaba/spring-cloud-alibaba · error · BusinessException
balance not enough
Error message
balance not enough
What it means
Thrown by OrderServiceImpl.createOrder (@GlobalTransactional) after the account Feign call returns a Result whose code equals COMMON_FAILED (2003). price is computed as count*2, and accountService.reduceBalance forwards it to the account branch; when the account branch fails (insufficient balance, optimistic-lock loss, or a downstream exception surfaced as Result.failed), the order orchestrator throws BusinessException("balance not enough"). This aborts the Seata global transaction, rolling back the already-applied stock deduction.
Source
Thrown at spring-cloud-alibaba-examples/integrated-example/integrated-order/src/main/java/com/alibaba/cloud/integration/order/service/impl/OrderServiceImpl.java:79
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);
order.setCreateTime(new Timestamp(System.currentTimeMillis()));
order.setUpdateTime(new Timestamp(System.currentTimeMillis()));
orderMapper.saveOrder(order);
logger.info("[createOrder] orderId: {}", order.getId());
return Result.success(order);
}
}
View on GitHub (pinned to 115d590110)
Solutions
- Verify funds: SELECT money FROM account WHERE user_id = '<userId>'; raise if needed via UPDATE account SET money = <value> WHERE user_id = '<userId>'.
- Lower the order count so count*2 <= money.
- Confirm the account service is healthy and its reduceBalance returns Result.success on success rather than a failed Result.
Defensive patterns
Strategy: validation
Validate before calling
// Before createOrder, ensure the account can cover price = count*2.
int price = count * 2;
Result<?> acctResult = accountService.getRemainAccount(userId);
Integer money = (Integer) acctResult.getData();
if (money == null || money < price) {
return Result.failed("balance not enough");
} Try / catch
try {
orderService.createOrder(userId, commodityCode, count);
} catch (BusinessException e) {
// 'balance not enough' -> Seata global rollback already ran; return user-facing error
} Prevention
- Pre-check funds with the account query endpoint.
- Seed account money above the maximum price (count*2) you will request.
- Remember createOrder deducts stock first, so a balance failure rolls back stock.
When it happens
Trigger: createOrder where count*2 exceeds the user's account money; the account branch returns code 2003 (COMMMON_FAILED) because checkBalance failed, the optimistic UPDATE matched 0 rows, or the account service threw. The order service then triggers global rollback to undo the stock deduction.
Common situations: Account seeded with insufficient money for price=count*2; the balance was consumed by a concurrent order; a misconfigured account service returning a failed Result.
Related errors
AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14).
Data as JSON: /api/errors/729a2fb52157b3e0.
Report an issue: GitHub.