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.

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.

Appendix: 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)