{"record":{"id":"729a2fb52157b3e0","repo":"alibaba/spring-cloud-alibaba","slug":"balance-not-enough","errorCode":null,"errorMessage":"balance not enough","messagePattern":"balance 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":79,"sourceCode":"\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);\n\t\torder.setCreateTime(new Timestamp(System.currentTimeMillis()));\n\t\torder.setUpdateTime(new Timestamp(System.currentTimeMillis()));\n\t\torderMapper.saveOrder(order);\n\t\tlogger.info(\"[createOrder] orderId: {}\", order.getId());\n\n\t\treturn Result.success(order);\n\t}\n\n}\n","sourceCodeStart":61,"sourceCodeEnd":97,"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#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before createOrder, ensure the account can cover price = count*2.\nint price = count * 2;\nResult<?> acctResult = accountService.getRemainAccount(userId);\nInteger money = (Integer) acctResult.getData();\nif (money == null || money < price) {\n    return Result.failed(\"balance not enough\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    orderService.createOrder(userId, commodityCode, count);\n} catch (BusinessException e) {\n    // 'balance not enough' -> Seata global rollback already ran; return user-facing error\n}","preventionTips":["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."],"tags":["seata","distributed-transaction","orchestration","account","global-rollback"],"backgroundTag":null,"analyzedSha":"115d5901102009492e05d5ec18c3f79cad4077d0","analyzedAt":"2026-08-14T04:47:13.900Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}