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

  1. Verify funds: SELECT money FROM account WHERE user_id = '<userId>'; raise if needed via UPDATE account SET money = <value> WHERE user_id = '<userId>'.
  2. Lower the order count so count*2 <= money.
  3. 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

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.