FuelLabs/fuels-ts · error · FuelError

COINS_ASSET_ID_MISMATCH

COINS_ASSET_ID_MISMATCH

Error message

All coins to consolidate must be from the same asset id.

What it means

Thrown by validateConsolidationTxsCoins when the coins array contains UTXOs whose assetId does not match the assetId being consolidated. Consolidation builds one transaction against a single asset, so any foreign-asset coin would corrupt the output accounting. This guard runs in both the base-asset and non-base-asset assembly paths.

Source

Thrown at packages/account/src/account.ts:1279

  /** @hidden * */
  private validateTransferAmount(amount: BigNumberish) {
    if (bn(amount).lte(0)) {
      throw new FuelError(
        ErrorCode.INVALID_TRANSFER_AMOUNT,
        'Transfer amount must be a positive number.'
      );
    }
  }

  /** @hidden * */
  private validateConsolidationTxsCoins(coins: Coin[], assetId: string) {
    if (coins.length <= 1) {
      throw new FuelError(ErrorCode.NO_COINS_TO_CONSOLIDATE, 'No coins to consolidate.');
    }

    if (!coins.every((c) => c.assetId === assetId)) {
      throw new FuelError(
        ErrorCode.COINS_ASSET_ID_MISMATCH,
        'All coins to consolidate must be from the same asset id.'
      );
    }
  }

  /** @hidden * */
  private async setTransactionStateForConnectors(params: {
    transactionRequest: TransactionRequest;
    connectorOptions: AccountSendTxParams;
  }): Promise<{
    transactionRequest: TransactionRequest;
    connectorsSendTxParams: FuelConnectorSendTxParams;
  }> {
    const { transactionRequest: requestToPrepare, connectorOptions } = params;

    const { onBeforeSend, skipCustomFee = false } = connectorOptions;

View on GitHub (pinned to b3f37c91ac)

Solutions

  1. Always fetch coins with the asset filter: provider.getCoins(account.address, assetId).
  2. If using a cached list, filter it right before the call: coins.filter(c => c.assetId === assetId).
  3. Group coins by assetId first and consolidate each group separately.

Example fix

// before
const { coins } = await provider.getCoins(account.address);
await account.consolidateCoins({ assetId });

// after
const { coins } = await provider.getCoins(account.address, assetId);
await account.consolidateCoins({ assetId });
Defensive patterns

Strategy: validation

Validate before calling

// Always fetch coins with the asset filter so the array is homogeneous
const { coins } = await provider.getCoins(account.address, assetId);
await account.consolidateCoins({ assetId });

// or sanitize an existing list:
const homogeneous = coins.filter(c => c.assetId === assetId);

Type guard

function allSameAsset(coins: Coin[], assetId: string): boolean {
  return coins.every(c => c.assetId === assetId);
}

Try / catch

try {
  await account.consolidateCoins({ assetId });
} catch (e) {
  if (e instanceof FuelError && e.code === ErrorCode.COINS_ASSET_ID_MISMATCH) {
    // re-fetch with the correct asset filter and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a coins array (or letting getCoins fetch one) that mixes assetIds, then calling account.consolidateCoins({ assetId }) or account.assembleNonBaseAssetConsolidationTxs({ assetId, coins }) where not every coin.assetId === assetId.

Common situations: Fetching all coins for an address (no asset filter) and feeding them to consolidateCoins for a specific asset; mutating assetId mid-loop; off-by-one when slicing a cached coin list.

Related errors


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