FuelLabs/fuel-core · error · anyhow::Error

The block has more than `u16::MAX` transactions, {}

Error message

The block has more than `u16::MAX` transactions, {}

What it means

The off-chain worker indexes each transaction's position in a block as a u16 (the owned-transaction index layout reserves 2 bytes for tx_idx). enumerate() yields a usize, and u16::try_from fails when a block contains more than 65535 transactions. Consensus limits keep valid Fuel blocks far below this, so the error indicates a hand-crafted or malformed block rather than normal chain data.

Source

Thrown at crates/fuel-core/src/graphql_api/worker_service.rs:367

    Ok(())
}

/// Associate all transactions within a block to their respective UTXO owners
fn index_tx_owners_for_block<T>(
    block: &Block,
    block_st_transaction: &mut T,
    chain_id: &ChainId,
) -> anyhow::Result<()>
where
    T: OffChainDatabaseTransaction,
{
    for (tx_idx, tx) in block.transactions().iter().enumerate() {
        let block_height = *block.header().height();
        let inputs;
        let outputs;
        let tx_idx = u16::try_from(tx_idx).map_err(|e| {
            anyhow::anyhow!("The block has more than `u16::MAX` transactions, {}", e)
        })?;
        let tx_id = tx.id(chain_id);
        match tx {
            Transaction::Script(tx) => {
                inputs = tx.inputs().as_slice();
                outputs = tx.outputs().as_slice();
            }
            Transaction::Create(tx) => {
                inputs = tx.inputs().as_slice();
                outputs = tx.outputs().as_slice();
            }
            Transaction::Mint(_) => continue,
            Transaction::Upgrade(tx) => {
                inputs = tx.inputs().as_slice();
                outputs = tx.outputs().as_slice();
            }
            Transaction::Upload(tx) => {
                inputs = tx.inputs().as_slice();

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Reject the block before processing if block.transactions().len() > u16::MAX.
  2. For test chains, cap transactions per block below 65535.
  3. If raising the real limit is required, the index key layout (2-byte tx_idx) must be widened — a coordinated storage-format change.

Example fix

// before
let tx_idx = u16::try_from(tx_idx)?;

// after
if block.transactions().len() > u16::MAX as usize {
    anyhow::bail!("block exceeds maximum supported transaction count");
}
let tx_idx = u16::try_from(tx_idx)?;
Defensive patterns

Strategy: validation

Validate before calling

// Reject oversized blocks before execution/indexing.
if block.transactions().len() > u16::MAX as usize {
    anyhow::bail!(
        "block {} has {} transactions; max supported is {}",
        block.header().height(),
        block.transactions().len(),
        u16::MAX
    );
}

Type guard

fn is_indexable_block(block: &Block) -> bool {
    block.transactions().len() <= u16::MAX as usize
}

Try / catch

match process_block(&block, &mut tx, &chain_id) {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("u16::MAX transactions") => {
        // malformed/hand-crafted block: reject it, never retry
        Err(anyhow::anyhow!("rejecting malformed block: {}", e))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Processing (importing/executing) a block whose transactions() iterator yields more than u16::MAX items — custom-built blocks, fuzzing, or a changed protocol limit not reflected in the index layout.

Common situations: Test/fuzz environments injecting oversized blocks; local dev chains with relaxed limits; future consensus parameter changes raising max txs per block.

Related errors


AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16). Data as JSON: /api/errors/fa9a6659c3a6d16b. Report an issue: GitHub.