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

Failed to read stream entries from Redis node: {e}

Error message

Failed to read stream entries from Redis node: {e}

What it means

Same batch-read path (crates/fuel-core/src/service/adapters/consensus_module/poa.rs:715) but Redis answered with an error ({e}) rather than timing out. The node's cached connection is reset and its whole read counts as failed; if the remaining successful reads drop below quorum, the caller aborts with the 'Cannot reconcile' error.

Source

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

        let count = u32::try_from(max_entries).unwrap_or(u32::MAX);
        let stream_entries = timeout(
            self.node_timeout,
            redis::Script::new(READ_STREAM_ENTRIES_SCRIPT)
                .key(&self.block_stream_key)
                .arg(next_height)
                .arg(count)
                .invoke_async::<Vec<(u32, u64, Vec<u8>, String)>>(&mut connection),
        )
        .await;

        let entries = match stream_entries {
            Err(_) => {
                self.clear_cached_connection(redis_node).await;
                return Err(anyhow!("Timed out reading stream entries from Redis node"));
            }
            Ok(Err(e)) => {
                self.clear_cached_connection(redis_node).await;
                return Err(anyhow!(
                    "Failed to read stream entries from Redis node: {e}"
                ));
            }
            Ok(Ok(entries)) => entries,
        };

        let mut blocks = Vec::new();
        for (height, epoch, bytes, _stream_id) in entries {
            match postcard::from_bytes::<SealedBlock>(&bytes) {
                Ok(block) => blocks.push((height, epoch, block)),
                Err(e) => {
                    tracing::warn!(
                        "Skipping stream entry: failed to deserialize block at height {height}: {e}"
                    );
                }
            }
        }

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Act on the {e} payload: free Redis memory (maxmemory) or disk, fix ACL/credentials
  2. Ensure {lease_key}:block:stream is actually a stream (WRONGTYPE → remove the foreign key)
  3. Return the node to a healthy primary state; quorum of remaining nodes keeps the chain alive meanwhile
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight server health per node:
// redis-cli INFO persistence -> rdb_last_bgsave_status:ok
// redis-cli CONFIG GET maxmemory / maxmemory-policy -> reads must not be rejected
// redis-cli TYPE <lease_key>:block:stream -> stream

Try / catch

match read_entries(node, h, n).await {
    Err(e) if e.to_string().contains("Failed to read stream entries") => {
        // parse {e}: WRONGTYPE/MISCONF/OOM/NOAUTH each map to a specific op action;
    }
    other => other,
}

Prevention

When it happens

Trigger: Redis errors during the XRANGE-style batch read: WRONGTYPE on block_stream_key, MISCONF persistence failure, NOAUTH/ACL denial, OOM command rejection under maxmemory, or read-only replica errors.

Common situations: Redis maxmemory reached so reads are rejected; disk-full MISCONF state; permission/ACL changes; the stream key overwritten by non-stream data.

Related errors


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