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

Cannot repair block because fencing token is not initialized

Error message

Cannot repair block because fencing token is not initialized

What it means

repair_sub_quorum_block needs the fencing epoch to stamp its Lua writes; current_epoch_token is None, meaning this node never acquired the Redis leader lease (or already released it via release_if_owner). Without a valid epoch the fencing checks in write_block.lua cannot prove write ownership, so repair is refused before any I/O.

Source

Thrown at crates/fuel-core/src/service/adapters/consensus_module/poa.rs:1176

    /// Uses `publish_block_on_all_nodes` which runs `write_block.lua`:
    /// - Written: node accepted the block (counted toward quorum)
    /// - HEIGHT_EXISTS: node has *some* block at this height — may be a
    ///   different block from a competing partial write, so NOT counted
    /// - FENCING_ERROR: lost the lock — abort the repair
    /// - The total (pre_existing + newly written) must reach quorum
    fn repair_sub_quorum_block(
        &self,
        block: &SealedBlock,
        pre_existing_count: usize,
    ) -> anyhow::Result<bool> {
        let epoch = match *self
            .current_epoch_token
            .lock()
            .map_err(|_| anyhow!("cannot access epoch token, poisoned lock"))?
        {
            Some(epoch) => epoch,
            None => {
                return Err(anyhow!(
                    "Cannot repair block because fencing token is not initialized"
                ));
            }
        };
        let block_data = postcard::to_allocvec(block)?;
        // Start from the pre-existing count (nodes already confirmed to
        // have this specific block during reconciliation). Only count
        // newly Written nodes — HeightExists means the node has *some*
        // block at this height, but it might be a different block from
        // a competing leader's partial write.
        let mut total_with_block = pre_existing_count;
        for result in self.publish_block_on_all_nodes(epoch, block, &block_data) {
            match result {
                Ok(WriteBlockResult::Written) => {
                    total_with_block = total_with_block.saturating_add(1);
                }
                Ok(WriteBlockResult::HeightExists) => {
                    // Node has some block at this height — may or may

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Ensure sequencing: acquire the lease first (can_produce_block) and only then run reconciliation/repair on this node.
  2. If leadership is gone, skip repair — the actual lease holder's reconciliation will repair or supersede these partial writes.
  3. Investigate the ordering bug if reconciliation still considers this node responsible while its token is None (e.g. repair invoked after release_if_owner).
  4. In tests, run one successful acquire_lease_if_free before exercising repair.
Defensive patterns

Strategy: validation

Validate before calling

// Before reconciliation/repair: confirm this node actually holds the lease
// (token initialized) — e.g. drive can_produce_block() to true first.
if !adapter.can_produce_block().await? {
    // Not the leader — skip repair; the lease holder will reconcile.
    return Ok(());
}

Try / catch

// If the error still surfaces, map it to 'skip this block' rather than
// failing the whole reconciliation loop.
if let Err(e) = repair_result {
    if e.to_string().contains("fencing token is not initialized") {
        continue; // next block; not fatal
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Reconciliation on a node whose can_produce_block/acquire_lease_if_free never succeeded (or whose release_if_owner already cleared the token) encounters partial writes attributed to this node and attempts repair.

Common situations: A node that lost leadership before finishing its writes tries to reconcile; or reconciliation runs on startup before the first lease acquisition completes; or test setups that call reconciliation without running leader election.

Related errors


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