Why
On staging, a 2000 USD wallet → card top-up failed because reserve.CARD_USD held 913.81. The app showed a raw native Alert: "Insufficient balance on account ". Nothing was debited; the transaction rolled back.
Backend
Error message
-
postDoubleEntrynow throwsInsufficientAccountBalanceException, which still extends BadRequestException. - The client-facing message no longer contains the account id. The id is logged server side instead.
Card deposit and card withdraw
- A CARD_USD system-account shortfall now returns 422
CARD_FUNDING_UNAVAILABLE. - That case also logs an
ALERT:line with a hint that depends on the account type:- reserve.CARD_USD: top up the KeySecure reserve
- normal.CARD_USD: mirror drift
- A shortfall on the user's wallet or card stays
INSUFFICIENT_BALANCE. - Withdraw: a user asking for more than their card balance gets
INSUFFICIENT_BALANCE, with no false ALERT. This applies only to a real CARD_USD shortfall error; DB and lock errors are not remapped.
Frontend
- A new
funding-unavailablekind maps the new code, with separate top-up and withdraw copy. -
transfer-form.tsxnow shows every error as a toast throughshowErrorMessageinstead ofAlert.alert.- Raw backend text is never shown, except the wrong-passcode message.
- The sheets are closed before the toast appears.
Verification
- be: card-account and transactions jest pass (300 card-account tests);
yarn buildis clean. - fe: type-check is clean, lint has 0 errors, transfer and card-account tests pass.
- The Opus review gate passed in round 2. The NIT fixes come after it.
- Pre-existing and unrelated:
admin-lending.risk.spec.tsfails at BASE with a DI error.
Follow-ups
- Two concurrent withdraws can very rarely trigger a false ALERT, because the account snapshot is read before the transaction.
- The wrong-passcode toast sits under the passcode sheet backdrop.