fullstackhero/dotnet-starter-kit · error · InvalidOperationException

Insufficient wallet balance.

Error message

Insufficient wallet balance.

What it means

Wallet.Dbit throws InvalidOperationException when the requested debit amount exceeds the wallet's current balance — i.e. Balance.Subtract(amount) would go negative. The wallet does not support overdrafts.

Solutions

  1. Check wallet.Balance >= amount before calling Debit, or surface a 'top up required' flow.
  2. Top up the wallet (Credit) to cover the amount.
  3. Serialize debits per wallet (row lock / optimistic concurrency) to prevent overdraft races.
  4. Verify amount units and Money construction so the debit value is correct.

Example fix

// before
wallet.Debit(amount, kind, desc, refId); // throws when balance short

// after
if (wallet.Balance.Amount >= amount.Amount)
{
    wallet.Debit(amount, kind, desc, refId);
}
Defensive patterns

Strategy: validation

Validate before calling

if (wallet.Balance.Amount < amount.Amount) throw new InsufficientBalanceException(wallet.Balance, amount);

Try / catch

try { wallet.Debit(amount, kind, desc, refId); } catch (InvalidOperationException) { // prompt wallet top-up }

Prevention

When it happens

Trigger: Debiting an amount greater than Balance; two concurrent debits each valid individually but overdrafting together; debiting before a prior top-up/credit transaction was committed.

Common situations: Charging a customer whose wallet was not topped up; race conditions between parallel charge jobs on the same wallet; currency/amount unit mismatches (cents vs whole units) inflating the debit.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/676bbf6cfa5f4c2d. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Billing/Modules.Billing/Domain/Wallet.cs:55

        ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(amount.Amount, 0m);
        // Validate currency before touching the aggregate: Money.Add throws on a
        // mismatch, so computing the new balance first keeps a rejected credit from
        // leaving a phantom ledger row behind.
        var newBalance = Balance.Add(amount);
        var tx = WalletTransaction.Create(Id, TenantId, amount, kind, description, referenceId);
        _transactions.Add(tx);
        Balance = newBalance;
        UpdatedAtUtc = DateTime.UtcNow;
        return tx;
    }

    public WalletTransaction Debit(Money amount, WalletTransactionKind kind, string description, string? referenceId)
    {
        ArgumentNullException.ThrowIfNull(amount);
        ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(amount.Amount, 0m);
        var remaining = Balance.Subtract(amount);
        if (remaining.Amount < 0m)
            throw new InvalidOperationException("Insufficient wallet balance.");
        var tx = WalletTransaction.Create(Id, TenantId, amount.Multiply(-1m), kind, description, referenceId);
        _transactions.Add(tx);
        Balance = remaining;
        UpdatedAtUtc = DateTime.UtcNow;
        return tx;
    }
}

View on GitHub (pinned to 3f2959e683)