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

publish abandoned: quorum reached before this node responded

Error message

publish abandoned: quorum reached before this node responded

What it means

publish_block_on_all_nodes fans write_block.lua out to every Redis node and returns one result slot per node; when quorum of Written results arrives it breaks and drops the receiver, so slots for still-in-flight nodes stay None and are rendered as this placeholder error. It records 'result discarded because success was already assured', not a write failure — the caller already counted a quorum of successes.

Source

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

            if matches!(result, Ok(WriteBlockResult::Written)) {
                written_count = written_count.saturating_add(1);
            }
            results[idx] = Some(result);
            received = received.saturating_add(1);

            if self.quorum_reached(written_count) {
                // Quorum reached. Return immediately; any threads still
                // running are abandoned. Their later `tx.send` will fail
                // silently because we drop the receiver below.
                break;
            }
        }

        results
            .into_iter()
            .map(|r| {
                r.unwrap_or_else(|| {
                    Err(anyhow!(
                        "publish abandoned: quorum reached before this \
                         node responded"
                    ))
                })
            })
            .collect()
    }

    /// `'static` helper that runs `write_block.lua` against a single node.
    /// Free function (no `&self`) so detached threads spawned by
    /// `publish_block_on_all_nodes` can run it without borrowing the adapter.
    #[allow(clippy::too_many_arguments)]
    fn invoke_write_block_script(
        redis_client: &redis::Client,
        node_timeout: Duration,
        block_stream_key: &str,
        epoch_key: &str,
        lease_key: &str,

View on GitHub (pinned to b9d4d170da)

Solutions

  1. In callers, treat this message as 'skipped', not failure: count only Ok(WriteBlockResult::Written) and compare against quorum, exactly as publish_produced_block (poa.rs:1379-1394) already does.
  2. If you need every node's outcome, remove the early break and await all senders instead of only quorum.
  3. Tune per-node Redis command timeouts so stragglers answer within the publish window.

Example fix

// before
let all_ok = results.into_iter().all(|r| r.is_ok()); // misreads abandoned as failure

// after — only Written counts, abandoned slots are ignored
let written = results
    .into_iter()
    .filter(|r| matches!(r, Ok(WriteBlockResult::Written)))
    .count();
let ok = self.quorum_reached(written);
Defensive patterns

Strategy: fallback

Try / catch

// Filter the benign 'abandoned' placeholder out before aggregating results.
let meaningful: Vec<_> = results.into_iter().filter(|r| {
    !matches!(r, Err(ref e) if e.to_string().contains("publish abandoned"))
}).collect();

Prevention

When it happens

Trigger: A quorum of fast Redis nodes acknowledge the write while at least one slower node has not answered; its slot is None when the loop breaks, so the aggregate Vec contains this error for that node.

Common situations: One Redis endpoint with higher latency (cross-datacenter, overloaded, pause) during block publish or sub-quorum repair. Callers that propagate Err from the aggregate naively misread this benign case as a real failure.

Related errors


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