eth_sendRawTransaction returns balance and nonce errors when the signed transaction fails the node's pre-submission checks. The node verifies the signature, compares the transaction nonce against the account's pending nonce, and checks that the balance covers gasLimit * effectiveFee + value plus the intrinsic gas floor. The error string maps directly to which check failed: 'insufficient funds for gas * price + value' means the upfront cost exceeds the balance, 'nonce too low' means the nonce is already used, 'nonce too high' means a gap exists ahead of it, and 'already known' means the exact transaction is already in the pool. This article provides a runnable Node.js diagnostic that parses the signed fields, fetches balance and pending nonce, and prints which check failed and by how much. It also covers failure modes such as lagging nodes, mempool differences, and in-flight transactions that make a balance correct at latest but short at pending.
The Node's Pre-Submission Checks in Order
When you call eth_sendRawTransaction, the node performs a sequence of checks before accepting the transaction into its mempool. The Ethereum JSON-RPC specification defines the method contract, but the exact order and error strings are client-specific and vary by provider. In practice, most clients follow this order: signature recovery, nonce validation against the account's pending nonce, and balance validation against the upfront cost.
The first check is signature recovery. The node recovers the sender address from the ECDSA signature and verifies it matches the expected format. If this fails, the error is typically 'invalid sender' or 'invalid signature'. This check does not involve balance or nonce, but it must pass before the others are evaluated.
The second check is nonce validation. The node compares the transaction nonce against the account's pending nonce, which includes transactions already in the mempool. If the transaction nonce is lower than the pending nonce, the node returns 'nonce too low'. If it is higher, the node returns 'nonce too high' because a gap exists. If the nonce matches but the exact transaction is already in the pool, the node returns 'already known'.
The third check is balance validation. The node computes the upfront cost as gasLimit multiplied by the effective fee, plus the value being transferred. For EIP-1559 transactions, the effective fee is the maxFeePerGas, not the maxPriorityFeePerGas. The node also enforces an intrinsic gas floor of 21000 plus calldata costs, as defined in the Ethereum Yellow Paper. If the balance is insufficient, the node returns 'insufficient funds for gas * price + value'.
The method contracts are defined by the Ethereum JSON-RPC specification for eth_sendRawTransaction, eth_getTransactionCount, eth_getBalance, and eth_estimateGas. The fee fields whose product against the gas limit determines the upfront cost come from EIP-1559, and the intrinsic gas floor (21000 plus calldata) is defined in the Ethereum Yellow Paper.
- Signature recovery: verifies the sender address from the ECDSA signature.
- Nonce validation: compares against the account's pending nonce from eth_getTransactionCount with the 'pending' block parameter.
- Balance validation: checks gasLimit * effectiveFee + value against the account balance.
- Intrinsic gas floor: 21000 plus calldata costs, per the Yellow Paper.
The Insufficiency Family: Balance Below Upfront Cost
The error 'insufficient funds for gas * price + value' means the account balance is less than the upfront cost the node computes. For legacy transactions, the upfront cost is gasLimit * gasPrice + value. For EIP-1559 transactions, the node uses maxFeePerGas * gasLimit + value, even though the actual effective fee may be lower. This is because the node must guarantee the sender can cover the maximum possible cost, as specified in EIP-1559.
A common mistake is to check the balance against the effective fee (baseFee + maxPriorityFeePerGas) and conclude the account has enough. The node checks the maximum, so a transaction with a high maxFeePerGas can fail even when the effective fee would be affordable. The corrective action is to either reduce the maxFeePerGas or increase the account balance.
Another variant is a balance that covers the value but not the fee. For example, if the account has exactly the value being sent but no extra for gas, the node returns the same 'insufficient funds' error. The diagnostic must separate the value component from the fee component to identify which part is short.
The intrinsic gas floor is also part of the upfront cost. Even if the gasLimit is set correctly, the node requires the balance to cover at least 21000 gas plus calldata costs. A transaction with a gasLimit below the intrinsic floor is rejected before the balance check, but if the gasLimit is above the floor, the balance must cover the full gasLimit * effectiveFee.
- Legacy: upfront cost = gasLimit * gasPrice + value.
- EIP-1559: upfront cost = gasLimit * maxFeePerGas + value, not the effective fee.
- Intrinsic gas floor: 21000 plus calldata costs, per the Yellow Paper.
- Corrective action: reduce maxFeePerGas or increase balance.
The Nonce Family: Too Low, Too High, and Already Known
The nonce errors are distinct and require different corrective actions. 'nonce too low' means the transaction nonce is lower than the account's pending nonce. This happens when the transaction has already been mined, or when a gap was filled by another transaction. The corrective action is to fetch the pending nonce again and resubmit with the correct value.
'nonce too high' means the transaction nonce is higher than the pending nonce, indicating a gap exists ahead of it. The node will not accept the transaction until the gap is filled. The corrective action is to either wait for the missing nonce to be filled or submit the missing transactions first.
'already known' means the exact transaction is already in the mempool. This is not an error in the traditional sense; it indicates the node has seen the transaction before. The corrective action is to wait for confirmation or replace the transaction with a higher fee if it is stuck. The replacement transaction underpriced and stuck transactions guide covers the replacement mechanics.
The pending versus latest nonce distinction is critical. eth_getTransactionCount with the 'pending' block parameter returns the nonce including mempool transactions, while 'latest' returns the nonce from the last mined block. A lagging node may return a 'latest' nonce that is behind the network, causing a spurious 'nonce too low' if you use the wrong parameter. Always use 'pending' for submission.
- nonce too low: nonce already used or gap filled; refetch pending nonce.
- nonce too high: gap exists ahead; fill the gap or wait.
- already known: exact transaction in pool; wait or replace.
- Use eth_getTransactionCount with 'pending' for submission, not 'latest'.
# Read pending and latest nonce, then the balance, before resubmitting.
curl -s -X POST "$RPC_URL" -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionCount","params":["0xYourAddress","pending"]}'
# Compare against latest: a gap between pending and latest means in-flight transactions.
curl -s -X POST "$RPC_URL" -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":2,"method":"eth_getTransactionCount","params":["0xYourAddress","latest"]}'
# eth_getBalance at pending is the value the node checks against gas*price+value.
curl -s -X POST "$RPC_URL" -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":3,"method":"eth_getBalance","params":["0xYourAddress","pending"]}'Recomputing Upfront Cost Before Resubmitting
Before resubmitting a failed transaction, recompute the upfront cost from the signed fields. Fetch the account balance with eth_getBalance and the pending nonce with eth_getTransactionCount. Then parse the signed transaction to extract gasLimit, maxFeePerGas (or gasPrice), and value. Compute the upfront cost as gasLimit * maxFeePerGas + value for EIP-1559, or gasLimit * gasPrice + value for legacy.
Compare the computed upfront cost against the balance. If the balance is short, the shortfall is the difference. If the nonce is the issue, compare the transaction nonce against the pending nonce. This recomputation avoids retry loops that blame the node when the transaction itself is malformed.
The EVM nonce management with eth_getTransactionCount guide provides additional context on nonce tracking. For fee estimation, the Estimating gas price with eth_feeHistory guide explains how to derive a safe maxFeePerGas. The eth_estimateGas and deriving a safe gas limit guide covers gas limit estimation.
- Fetch balance with eth_getBalance at 'pending'.
- Fetch nonce with eth_getTransactionCount at 'pending'.
- Parse signed fields: gasLimit, maxFeePerGas, value, nonce.
- Compute upfront cost and compare against balance.
Runnable Node.js Diagnostic for Balance and Nonce Errors
The following Node.js script parses a signed transaction, fetches the account balance and pending nonce, and prints which check failed and by how much. It uses ethers.js for RLP decoding and JSON-RPC calls. Replace the RPC_URL and RAW_TX constants with your own values.
The script computes the upfront cost for both legacy and EIP-1559 transactions, compares it against the balance, and checks the nonce against the pending nonce. It prints a diagnostic message for each error string, including the computed shortfall and the corrective action.
const { ethers } = require('ethers');
const RPC_URL = 'https://your-rpc-endpoint';
const RAW_TX = '0x...';
async function diagnose() {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const tx = ethers.Transaction.from(RAW_TX);
const from = tx.from;
const [balance, pendingNonce, latestNonce] = await Promise.all([
provider.getBalance(from, 'pending'),
provider.getTransactionCount(from, 'pending'),
provider.getTransactionCount(from, 'latest')
]);
const gasLimit = tx.gasLimit;
const value = tx.value;
const maxFee = tx.maxFeePerGas ?? tx.gasPrice;
const upfront = gasLimit * maxFee + value;
console.log('From:', from);
console.log('Balance:', ethers.formatEther(balance));
console.log('Pending nonce:', pendingNonce);
console.log('Latest nonce:', latestNonce);
console.log('Tx nonce:', tx.nonce);
console.log('Upfront cost:', ethers.formatEther(upfront));
if (balance < upfront) {
const shortfall = upfront - balance;
console.log('ERROR: insufficient funds for gas * price + value');
console.log('Shortfall:', ethers.formatEther(shortfall));
}
if (tx.nonce < pendingNonce) {
console.log('ERROR: nonce too low');
console.log('Expected nonce:', pendingNonce);
} else if (tx.nonce > pendingNonce) {
console.log('ERROR: nonce too high');
console.log('Gap ahead:', tx.nonce - pendingNonce);
}
}
diagnose().catch(console.error);Results Table for Measuring Against Your Own Endpoint
Use the following table to record the results of your diagnostic against your own endpoint. Fill in the error string, computed shortfall, pending nonce, latest nonce, and the corrective action. This table helps you track patterns across multiple submissions and identify whether the issue is balance-related or nonce-related.
Run the diagnostic script for each failed transaction and record the values. If the shortfall is positive, the balance is insufficient. If the pending nonce differs from the latest nonce, there are in-flight transactions. If the transaction nonce is lower than the pending nonce, the nonce is too low. If it is higher, the nonce is too high.
- Error string: the exact message returned by eth_sendRawTransaction.
- Computed shortfall: upfront cost minus balance, if positive.
- Pending nonce: from eth_getTransactionCount at 'pending'.
- Latest nonce: from eth_getTransactionCount at 'latest'.
- Corrective action: reduce maxFeePerGas, increase balance, or adjust nonce.
Failure Modes: Lagging Nodes, Mempool Differences, and In-Flight Transactions
A lagging node can return a 'latest' nonce that is behind the network, causing a spurious 'nonce too low' if you use the wrong block parameter. Always use 'pending' for submission. If the node is significantly behind, consider switching to a different endpoint. The Ethereum RPC URL and endpoint selection (RPC Assistant) guide covers endpoint selection.
Mempool differences can cause 'already known' on one endpoint and acceptance on another. This happens because different nodes have different mempool policies and propagation delays. If you see 'already known' on one endpoint, try another or wait for propagation. The Debugging JSON-RPC -32603 internal errors guide covers related error handling.
A balance that is correct at 'latest' but short at 'pending' indicates an in-flight transaction that has not yet been mined. The pending balance includes deductions for mempool transactions, so the available balance is lower. Always check the balance at 'pending' before resubmitting. If the balance is short at pending, wait for the in-flight transaction to confirm or replace it.
- Lagging node: use 'pending' nonce, not 'latest'.
- Mempool differences: 'already known' may vary by endpoint.
- In-flight transactions: balance at 'pending' is lower than 'latest'.
- Corrective action: wait, replace, or switch endpoints.
Limitations and Tradeoffs: Client-Specific Errors and Retry Implications
The exact error strings are client-specific and vary by provider. Geth, Erigon, Nethermind, and Besu may return slightly different messages for the same underlying condition. The Ethereum JSON-RPC specification defines the method contract, but not the error strings. Always treat the error string as a hint and verify the underlying condition with the diagnostic script.
A mempool-node difference can change the observed error for the same bytes. A transaction that is 'already known' on one node may be accepted on another. This has retry and idempotency implications: retrying the same transaction on a different endpoint may succeed, but it may also create a duplicate if the first endpoint eventually propagates it. Use a unique nonce per transaction and monitor for confirmation.
The retry loop should be idempotent. Before resubmitting, recompute the upfront cost and nonce. If the transaction is already in the pool, wait for confirmation rather than resubmitting. If the balance is short, increase the balance or reduce the fee. If the nonce is wrong, fetch the pending nonce again and resubmit with the correct value.
- Error strings are client-specific and vary by provider.
- Mempool differences can change the observed error for the same bytes.
- Retry loops should be idempotent: recompute before resubmitting.
- Use a unique nonce per transaction and monitor for confirmation.
Troubleshooting Checklist for Balance and Nonce Errors
When eth_sendRawTransaction fails, follow this checklist to identify the root cause. First, parse the signed transaction to extract the nonce, gasLimit, maxFeePerGas (or gasPrice), and value. Second, fetch the account balance and pending nonce. Third, compute the upfront cost and compare against the balance. Fourth, compare the transaction nonce against the pending nonce.
If the error is 'insufficient funds for gas * price + value', check whether the balance covers the upfront cost. If the balance covers the value but not the fee, the shortfall is the fee component. If the maxFeePerGas is high, reduce it or increase the balance. If the error is 'nonce too low', refetch the pending nonce and resubmit. If the error is 'nonce too high', fill the gap or wait. If the error is 'already known', wait for confirmation or replace the transaction.
For additional context on fee estimation, see the Estimating gas price with eth_feeHistory guide. For gas limit estimation, see the eth_estimateGas and deriving a safe gas limit guide. For nonce management, see the EVM nonce management with eth_getTransactionCount guide.
- Parse signed fields: nonce, gasLimit, maxFeePerGas, value.
- Fetch balance and pending nonce.
- Compute upfront cost and compare against balance.
- Compare transaction nonce against pending nonce.
- Apply corrective action based on the error string.
Next Steps: Monitoring, Automation, and Endpoint Selection
After resolving the immediate error, consider automating the diagnostic. Integrate the recomputation logic into your transaction submission pipeline to catch balance and nonce issues before they reach the node. Monitor the pending nonce and balance at regular intervals to detect in-flight transactions and shortfalls.
For production workloads, use a reliable RPC endpoint. The Ethereum RPC URL and endpoint selection (RPC Assistant) guide covers endpoint selection criteria. The RPC pricing page provides information on pricing plans. The API service page describes the API service offerings.
For more troubleshooting guides, visit the OnFinality Learn hub. The Ethereum network page provides network-specific details. The replacement transaction underpriced and stuck transactions guide covers the replacement error class, which is distinct from the balance and nonce errors covered here.
- Automate recomputation in your submission pipeline.
- Monitor pending nonce and balance regularly.
- Use a reliable RPC endpoint for production.
- Review related guides for replacement and fee estimation.