FuelLabs/fuels-ts · error · FuelError

MAX_FEE_TOO_LOW

MAX_FEE_TOO_LOW

Error message

Max fee '${setMaxFee}' is lower than the required: '${requiredMaxFee}'.

What it means

Thrown by setAndValidateGasAndFeeForAssembledTx when the caller-supplied setMaxFee is less than transactionRequest.maxFee (the minimum required max fee computed from gas limit, gas price, and byte size). maxFee is the user-declared ceiling the chain enforces; setting it below the required amount guarantees the tx would be rejected, so the SDK fails fast.

Source

Thrown at packages/account/src/providers/assemble-tx-helpers.ts:84

  const maxFeeSpecified = isDefined(setMaxFee);
  const isScriptTx = transactionRequest.type === TransactionType.Script;

  if (gasLimitSpecified && isScriptTx) {
    const requiredGasLimit = transactionRequest.gasLimit;
    if (bn(setGasLimit).lt(bn(requiredGasLimit))) {
      throw new FuelError(
        ErrorCode.GAS_LIMIT_TOO_LOW,
        `Gas limit '${setGasLimit}' is lower than the required: '${requiredGasLimit}'.`
      );
    }

    transactionRequest.gasLimit = bn(setGasLimit);
  }

  if (maxFeeSpecified) {
    const requiredMaxFee = transactionRequest.maxFee;
    if (bn(setMaxFee).lt(requiredMaxFee)) {
      throw new FuelError(
        ErrorCode.MAX_FEE_TOO_LOW,
        `Max fee '${setMaxFee}' is lower than the required: '${requiredMaxFee}'.`
      );
    }

    transactionRequest.maxFee = bn(setMaxFee);
  }

  if (gasLimitSpecified && !maxFeeSpecified) {
    const { maxFee: feeForGasPrice } = await provider.estimateTxGasAndFee({
      transactionRequest,
      gasPrice,
    });

    transactionRequest.maxFee = feeForGasPrice;
  }

  return transactionRequest;

View on GitHub (pinned to b3f37c91ac)

Solutions

  1. Omit setMaxFee so the SDK derives it from the current estimate.
  2. Read transactionRequest.maxFee after estimation and pass setMaxFee >= it.
  3. Re-estimate with provider.estimateTxGasAndFee before specifying setMaxFee.
  4. Add a margin to absorb gas-price variance: bn(requiredMaxFee).mul(12).div(10).

Example fix

// before
await account.assembleTx({ transactionRequest, setMaxFee: 1 });

// after
const { maxFee } = await provider.estimateTxGasAndFee({ transactionRequest, gasPrice });
await account.assembleTx({
  transactionRequest,
  setMaxFee: bn(maxFee).mul(12).div(10), // >= required
});
Defensive patterns

Strategy: validation

Validate before calling

import { bn } from '@fuel-ts/math';
function validateMaxFee(setMaxFee, requiredMaxFee) {
  if (bn(setMaxFee).lt(requiredMaxFee)) {
    throw new Error(`Max fee must be >= ${requiredMaxFee.toString()}`);
  }
}

Type guard

function maxFeeIsSufficient(setMaxFee, requiredMaxFee): boolean {
  return bn(setMaxFee).gte(bn(requiredMaxFee));
}

Try / catch

import { FuelError, ErrorCode } from '@fuel-ts/errors';
try {
  await account.assembleTx({ transactionRequest, setMaxFee });
} catch (e) {
  if (e instanceof FuelError && e.code === ErrorCode.MAX_FEE_TOO_LOW) {
    // bump setMaxFee to >= requiredMaxFee or omit it
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing setMaxFee below the estimated max fee when assembling/sending a transaction; setting a fixed low fee after gas-price spikes; computing max fee without including the byte/gas-price component.

Common situations: Hardcoding maxFee from a previous network state; gas price rose between estimation and submission; migrating to an SDK version that began enforcing the floor; underestimating fee for a large script tx.

Related errors


AI-assisted analysis of FuelLabs/fuels-ts@b3f37c91ac (2026-08-12). Data as JSON: /api/errors/26c9af9aab902cda. Report an issue: GitHub.