Intrinsic gas too low: cause and fix

Rejected before the chain. The node refuses it: no transaction hash, no receipt.

2 min read· Updated

Short answer

Every transaction needs a minimum amount of gas just to start: 21,000 for a plain transfer, more when it carries data or creates a contract. If the gas limit you set is below that, the node refuses it before anything runs. Do not set the limit by hand: let the client estimate it.

What you will see

Messages printed for it:

  • intrinsic gas too low
  • The amount of gas (1000) provided for the transaction is too low.
  • intrinsic gas too low: gas <your limit>, minimum needed <the minimum>
On this page

Where does it stop?

A transaction's path from your code to the node, the mempool, a block and a receipt. The node's check is marked in red with the error intrinsic gas too low, and the later steps are dashed: no hash and no receipt.A transaction's path from your code to the node, the mempool, a block and a receipt. The node's check is marked in red with the error intrinsic gas too low, and the later steps are dashed: no hash and no receipt.

The node checks the gas limit before it accepts the transaction. If the limit is below the minimum, nothing runs and nothing is mined: no hash, no receipt and no gas spent.

What is the minimum?

A standard ETH transfer needs a gas limit of 21,000. That is the base every transaction pays. Data adds to it (16 gas for each non-zero byte, EIP-2028), and so does creating a contract.

The least gas a transaction can start with: 21,000 for every transaction, plus calldata at 16 gas per non-zero byte, plus more for a contract creation. A gas limit of 20,999 is refused with intrinsic gas too low and 21,000 is accepted for a plain transfer.The least gas a transaction can start with: 21,000 for every transaction, plus calldata at 16 gas per non-zero byte, plus more for a contract creation. A gas limit of 20,999 is refused with intrinsic gas too low and 21,000 is accepted for a plain transfer.

This minimum is checked first, before the contract’s code runs. The gas the code then uses is a separate matter: if the limit covers the start but not the work, that is out of gas.

Why does it happen?

  • A hardcoded limit. A transfer’s 21,000 copied onto a call that carries data.
  • A number from a tool argument. An agent or a config sets gas: 1000 and nobody compares it with the minimum.
  • A limit for one kind of transaction used for another. A contract creation is more than a transfer.

How do I fix it?

  1. Do not set gas unless you must. With a local account, viem estimates the gas when you leave it out.
  2. If you set it, estimate first and add headroom:
import { createPublicClient, createWalletClient, http } from 'viem';
import { mainnet } from 'viem/chains';
declare const account: `0x${string}`;
declare const to: `0x${string}`;
declare const data: `0x${string}`;
const publicClient = createPublicClient({ chain: mainnet, transport: http() });
const wallet = createWalletClient({ account, chain: mainnet, transport: http() });
// Estimate the gas, then add headroom: the state can change before the transaction is mined.
const gas = await publicClient.estimateGas({ account, to, data });
await wallet.sendTransaction({ to, data, gas: (gas * 120n) / 100n });
  1. Read the minimum from the error where the client prints it. geth names it. If yours does not, estimate instead of guessing.

What does it look like in a trace?

A refusal by the node: a send span that ends with an error and no hash.

send 31337error
error.type
IntrinsicGasTooLowError
exception.message
The amount of gas (1000) provided for the transaction is too low.
blockchain.tx.hash
not set
From the run above, with a limit of 1,000. exception.message appears only with errorMessages set to sanitized.

By default only the error type is recorded, and for this refusal it is IntrinsicGasTooLowError. Nonce too low shows how to read the node’s wording too.

How this page was checked. Reproduced on a local Anvil chain (31337) with viem 2.57.2 and @hashspan/viem 1.1.0 on 2026-10-07: a plain transfer with a limit of 20,999 was refused and one with 21,000 was accepted. The third line above is geth's format, which names the minimum (geth txpool validation). Anvil only checked the 21,000 base in our run: a transaction with 32 bytes of data and a limit of 21,000 was accepted and never mined, so the cost of data comes from EIP-2028 and geth, not from our run.

FAQ

What is intrinsic gas?

The gas a transaction costs before its code runs. A standard ETH transfer takes 21,000 (ethereum.org). Data adds to it: a non-zero byte of calldata costs 16 gas (EIP-2028). geth also counts the access list and whether the transaction creates a contract.

How is this different from out of gas?

Intrinsic gas too low is a refusal at the node: no hash, no gas spent. Out of gas happens after the transaction was mined, when the code uses up the limit, and the gas is paid. See the out of gas page.

Does geth tell me the minimum?

Yes. Its error reads intrinsic gas too low: gas, then your limit, then minimum needed and the number. Anvil and viem print only the short form.

See it in your own trace

Type to search the docs, Learn topics and the blog.