Rippled: terINSUF_FEE_B

Created on 24 Oct 2017  路  19Comments  路  Source: ripple/rippled

I get from time to time a "terINSUF_FEE_B" error. But I am certain I have enough XRP to pay the fee. It happens when several transactions are sent at the same time.

I suspect it sums up all transactions for this account that are in the open ledger and on basis of that determines the possible balance ... So if I place an order to sell 30.000 XRP and also an order to sell 20.000 XRP in the same open ledger and I have 40.000 XRP in my account, then I would receive this error...... Is this correct and 'works as intended'?

Not completely sure, but this "total XRP spent" seems to be determined per orderbook. I.e. "if the total of XRP sell orders of one orderbook > XRP balance" then I receive the terINSUF_FEE_B. Strangely enough I can place 1 order manually where the XRP amount to be sold is larger then the balance.

I think I can work around it if I have to, but it seems non-intended.

Below an example trace of my logs. At the time, the account had +/- 85.000 XRP

[24/10/2017 06:54:37] Creating GTC order for Address: xxx - Sequence: 849786

= 849786
{
  "direction": "sell",
  "quantity": {
    "currency": "XRP",
    "value": "15000.00000000"
  },
  "totalPrice": {
    "currency": "BTC",
    "counterparty": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL",
    "value": "0.54636318"
  }
}

[24/10/2017 06:54:37] GTC order '849786' will cancel order '849780'

= 849786
{
  "resultCode": "terQUEUED",
  "resultMessage": "Held until escalated fee drops."
}

[24/10/2017 06:54:37] Creating GTC order for Address: xxx - Sequence: 849788

= 849788
{
  "direction": "sell",
  "quantity": {
    "currency": "XRP",
    "value": "36222.61903100"
  },
  "totalPrice": {
    "currency": "BTC",
    "counterparty": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL",
    "value": "1.35156036"
  }
}

[24/10/2017 06:54:37] GTC order '849788' will cancel order '849782'

= 849788
{
  "resultCode": "terQUEUED",
  "resultMessage": "Held until escalated fee drops."
}

[24/10/2017 06:54:38] Creating GTC order for Address: xxx - Sequence: 849790

= 849790
{
  "direction": "sell",
  "quantity": {
    "currency": "XRP",
    "value": "36222.61903100"
  },
  "totalPrice": {
    "currency": "BTC",
    "counterparty": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL",
    "value": "1.48028039"
  }
}

[24/10/2017 06:54:38] GTC order '849790' will cancel order '849784'

= 849790
{
  "resultCode": "terINSUF_FEE_B",
  "resultMessage": "Account balance can't pay fee."
}

Most helpful comment

@jnr101 Thank you for reporting this issue. It's been a while, but a fix is included in the just-release version 1.2.0

All 19 comments

Not my area of expertise, but I may be able to offer you some guidance.

  1. The fee charged by the XRP Ledger is dynamic inside of a given ledger, not just from ledger-to-ledger. As transactions are added to the ledger beyond a threshold the fee goes up exponentially. Under unusual circumstances the fee has jumped as high as 16,000 XRP (see https://xrpcharts.ripple.com/#/metrics). So an 85 XRP balance truly may not be enough to cover the required fee.

  2. Here is more information on the dynamic nature of fees: https://ripple.com/build/transaction-cost/. There are a number of network-wide and local-to-the-client metrics that determine the fee you must pay for your transaction to get onto the network. In particular, submitting several transactions at once can increase the load on an individual ledger and cause the immediate fee to increase. FWIW, the fee is not influenced by the order book you are using.

  3. If I'm understanding your logs correctly, you are not specifying the maximum fee that you are willing to pay. It might be prudent to specify that maximum, given the potential volatility of fees.

  4. Do you understand that the error code returned when a transaction is submitted is not the final disposition of the transaction? In particular, a ter code means the transaction will be automatically _retried_ later. So your log is not telling you whether the transaction actually got into the ledger. To do so you'd want to see if the hash for that transaction is present in a validated ledger. For robust transaction handling you cannot just send-and-forget a transaction; you must check back to locate the transaction in a validated ledger.

  5. To prevent your transaction from getting stuck in an unknown state forever you should set an expiration time for the transaction using a LastLedgerSequence field.

  6. Here is a thorough writeup on how to robustly submit transactions to the network: https://ripple.com/build/reliable-transaction-submission/. Take it to heart. Smart people have lost money if they have not followed all of the advice.

In summary, I believe all of the effects you are seeing are fully intended by the designers of the XRP Ledger. Best of luck.

Hi Scott, first of all, thanks for taking the time for your thorough answer.
The first thing to make clear is the amount of XRP. It is not 85, but 85 thousand! I find it unlikely that the fee rises to that level. As matter of fact, I have more bots running and this error only occurs on orderbooks dealing with XRP.
I do not set the max fee, but calculate the fee for every transaction, to a certain max level. The logs you see are from ripple-lib, they might differ a bit compared to the rippled json objects.
The error code is indeed 'ter', which has some side effects for the other trades I make (the sequence numbering becomes out of sync in my program.) I am aware that the transaction might be validated in the next ledger. That is not really an issue. I am placing orders as a market maker. If the order doesn't make it in this ledger then the next ledger is ok. And I replace older orders. And yes, I also use LastLedgerSequence field. But that is beside the point :-) no need to explain my program in all details.

Main issue is I think, that the amount of XRP in my wallet was 85 thousand, not 85 ...

The terINSUF_FEE_B error means that the transaction engine found some combination of pending or previous transactions that left your account balance insufficient to pay the fee. If you didn't impose any fee limit at all (never a good idea) it is possible that an instantaneous spike in what the server thought was the required fee caused the error.

Are you choosing the fee or are you letting the server choose it? If the latter, are you imposing any fee limits? You should never let the server choose the fee without imposing a fee limit. If you do, a server that has even momentarily poor synchronization to the network can recommend an absurdly high fee. The server relies on the client for fee sanity checking.

We do not recommend you let the server sign the transaction. And when you get an error like this, logging the exact transaction you submitted is helpful. If the fee is absurdly high, that's your problem. If the fee is low, then we know that for some reason the server thought your balance would be low, and we can investigate/troubleshoot that.

As said, I use ripple-lib, so it might be a little different then what you are used to in rippled. I set the fee as follows:

let prepared = await api.prepareOrder(address, order, { "sequence": mySequence, "fee": bots.fee } )

where bots.fee is a certain amount I set (max 0.2). Setting this fee sets the exact amount of fee to be used according to documentation. I could also set the maxFee next to this one, but it seems a little redundant, because of the exact setting of the fee.

The rest of my program is as follows:

const {signedTransaction} = api.sign(prepared.txJSON, secret[address]);
let result = await api.submit(signedTransaction)

I will see if I can capture the 'prepared' field next time in the log, hope it clears things up

That is odd. If you are specifying the exact fee and get terINSUF_FEE_B, that means that the server really did think that your XRP balance would be insufficient to pay the fee. That is the only condition that returns this exact message, unless ripple-lib issues it itself for some reason.

It could be that you had an offer to sell XRP and the server thought that a transaction would claim that offer. But offers being claimed by others can't push you below your reserve, and it seems that your reserve should have been enough to pay the fee on all outstanding transactions.

Problem is I can't put the log level of my rippled to 'trace' for a long time (it would show the balance and feePaid). But I can create more log from within my nodejs program. When it has occured again I'll be back with some more detailed log

I see what's going on. The balance predictor estimates the maximum cost of each transaction independently and doesn't fully take into account the fact that a transaction can't cost into the reserve. So if an offer is in the fee queue, it is assumed that it could be fully satisfied when it executes.

So if you have an offer in the queue that can pay out up to 30,000 XRP, your balance will be 30,000 XRP lower when the next transaction is tested for queuing.

The key here is that the first two transactions are offers to sell XRP (if I'm reading this correctly), and they _both got queued_ (terQUEUED).

Because those first two transactions are presumably still in the queue, when you send the third one, the queue checks that you have sufficient XRP to cover the maximum possible amount of XRP that the queued transactions could spend plus the fee of the third. Because you're offering to sell XRP, the queue assumes that your offers are completely taken when checking if more transactions are queued. Note this is only relevant to the queue, because we don't want to queue and broadcast a transaction that may or may not be able to claim its fee. Once the transaction gets out of the queue into the open ledger, then we know what the actual balance is likely to be, so further transactions can then be processed.

So 15000 + 36222.619031 = 51222.619031 plus whatever fees you're paying. Since there's no information in that output about the fees paid, I can't tell how close you're getting to your balance, but it does seem like you may be paying some pretty high fees.

@ximinez Yeah, that's what's going on. The maximum possible cost calculation is very worst case here and doesn't take into account the fact that the reserve is protected. I'm not sure whether it's worth adding code to fix that, but it's fairly simple and would not require an amendment.

The fix would be to take the return value of calculateMaxSpend and the amount of XRP in the account over its reserve and use the minimum of those two values.

Here's an argument for why it's worth fixing: It's perfectly reasonable to have several pending transactions to sell the same XRP. Without this fix, it can be harder than it should be to cancel one of them or add another one.

ok. The amount was +/- 85000. When adding 15000 + 36222 + again 36222 then that would take me over the 85000. That was the pattern I thought to see occuring every time. The fees shouldn't be higher then 0.2 XRP

So I just checked my logs again (no added loginfo yet, sorrie) to find out it also occured on order for orderbook BTC->XRP which is not what I first expected, but I thought better share it maybe you do see logic in it :)

It concerns the following order :

[25/10/2017 07:57:33] BTC.Bitstamp_XRP - Create GTC Order: Sell 0.526465 BTC for 16413.303803 XRP(Price: 31176.438705, fee: 0.000012)

[25/10/2017 07:57:33] Creating GTC order for Address: xxx - Sequence: 854860

# 854860
{
  "direction": "sell",
  "quantity": {
    "currency": "BTC",
    "counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
    "value": "0.52646500"
  },
  "totalPrice": {
    "currency": "XRP",
    "value": "16413.30380259"
  }
}

[25/10/2017 07:57:33] GTC order '854860' will cancel order '854851'

# 854860
{
  "resultCode": "terINSUF_FEE_B",
  "resultMessage": "Account balance can't pay fee."
}

And the other related orders sent just before that:

[25/10/2017 07:57:33] Creating GTC order for Address: xxx - Sequence: 854854

# 854854
{
  "direction": "sell",
  "quantity": {
    "currency": "BTC",
    "counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
    "value": "0.40000000"
  },
  "totalPrice": {
    "currency": "XRP",
    "value": "11115.07814684"
  }
}

[25/10/2017 07:57:33] GTC order '854854' will cancel order '854845'

# 854854
{
  "resultCode": "terQUEUED",
  "resultMessage": "Held until escalated fee drops."
}

[25/10/2017 07:57:33] Creating GTC order for Address: xxx - Sequence: 854857

# 854857
{
  "direction": "sell",
  "quantity": {
    "currency": "BTC",
    "counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
    "value": "0.52646500"
  },
  "totalPrice": {
    "currency": "XRP",
    "value": "14986.05999367"
  }
}

[25/10/2017 07:57:33] GTC order '854857' will cancel order '854848'
[25/10/2017 07:57:33] MGN:0.050000 IOC:28465.444035/5.00 GTC:27787.695367/0.40(3) BAL:0.526465 BID:D/26963.552610

# 854857
{
  "resultCode": "terQUEUED",
  "resultMessage": "Held until escalated fee drops."
}

The account contains 71220.103650 XRP and 0.526 BTC.

Well, that might be not the whole story, as also other trades were made, where XRP was sold for something else. The complete list of orders for that account at that particular moment:

[25/10/2017 07:57:33] XRP_BTC.Fifth - Create GTC Order: Sell 15000.000000 XRP for 0.567135 BTC(Price: 0.000038, fee: 0.000012)
[25/10/2017 07:57:33] BTC.Bitstamp_XRP - Create GTC Order: Sell 0.400000 BTC for 11115.078147 XRP(Price: 27787.695367, fee: 0.000012)
[25/10/2017 07:57:33] XRP_BTC.Bitstamp - Create GTC Order: Sell 15000.000000 XRP for 0.567135 BTC(Price: 0.000038, fee: 0.000012)
[25/10/2017 07:57:33] XRP_BTC.Fifth - Create GTC Order: Sell 21220.103650 XRP for 0.821879 BTC(Price: 0.000039, fee: 0.000012)
[25/10/2017 07:57:33] BTC.Bitstamp_XRP - Create GTC Order: Sell 0.526465 BTC for 14986.059994 XRP(Price: 28465.444035, fee: 0.000012)
[25/10/2017 07:57:33] XRP_BTC.Bitstamp - Create GTC Order: Sell 21220.103650 XRP for 0.821879 BTC(Price: 0.000039, fee: 0.000012)
[25/10/2017 07:57:33] XRP_BTC.Fifth - Create GTC Order: Sell 21220.103650 XRP for 0.900154 BTC(Price: 0.000042, fee: 0.000012)
[25/10/2017 07:57:33] BTC.Bitstamp_XRP - Create GTC Order: Sell 0.526465 BTC for 16413.303803 XRP(Price: 31176.438705, fee: 0.000012)
[25/10/2017 07:57:33] XRP_BTC.Bitstamp - Create GTC Order: Sell 21220.103650 XRP for 0.900154 BTC(Price: 0.000042, fee: 0.000012)

So could be maximum cost calculation, which hit in this case a BTC->XRP order.

My work-around would be to keep track of how much XRP I place in an XRP sell order and then subtract that from the 'available XRP' for the next trade in that same ledger

That seems like a reasonable workaround if you're only doing 3 transactions at a time. Good luck!

@scottschurr thanks for the links anyways. As it turns out, the answer is in there: https://ripple.com/build/transaction-cost/#queued-transactions:

Queuing Restrictions

The rippled server uses a variety of heuristics to estimate which transactions are "likely to be included in a ledger." The current implementation uses the following rules to decide which transactions to queue:

Transactions must be properly-formed and authorized with valid signatures.
Transactions with an AccountTxnID field cannot be queued.
A single sending address can have at most 10 transactions queued at the same time. In order for a transaction to be queued, the sender must have enough XRP to pay all the XRP costs of all the sender's queued transactions including both the Fee fields and the sum of the XRP that each transaction could send. New in: rippled 0.32.0
If a transaction affects how the sending address authorizes transactions, no other transactions from the same address can be queued behind it. New in: rippled 0.32.0
If the transaction includes a LastLedgerSequence field, the value of that field must be at least the current ledger index + 2.

The question in this case is imo, should an order for trading XRP account the same as a definite sending XRP

@ximinez, thanks, I will implement the workaround then. Can you let me know the verdict, will there come a 'fix'?

Are you talking about @JoelKatz's fix described above? I'll look into it, and get something going, but the soonest any change could reasonably be released is 0.90.

Yes. No problem, it's not a critical issue for me, and I can work around. Fix only if you are sure it should be fixed and that the fix is correct.

That's how we do all of our fixes. 馃榾

@jnr101 Thank you for reporting this issue. It's been a while, but a fix is included in the just-release version 1.2.0

Thanks @ximinez!

You're welcome @jnr101!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tuloski picture tuloski  路  8Comments

dmitriano picture dmitriano  路  9Comments

mDuo13 picture mDuo13  路  5Comments

RichardAH picture RichardAH  路  5Comments

nbougalis picture nbougalis  路  3Comments