nautechsystems/nautilus_trader · info

Sync cancelled

Error message

Sync cancelled

What it means

sync_blocks runs the block fetch loop inside tokio::select! against the client's cancellation token. When cancellation fires (disconnect, reconfiguration, or shutdown), the sync aborts deliberately with Err("Sync cancelled") — it signals an interrupted backfill of historical blocks into the local timestamp cache, not an unexpected failure.

Source

Thrown at crates/adapters/blockchain/src/data/core.rs:427

            .request_blocks_stream(from_block, Some(to_block))
            .await;

        tokio::pin!(blocks_stream);

        let mut metrics = BlockchainSyncReporter::new(
            BlockchainSyncReportItems::Blocks,
            from_block,
            total_blocks,
            BLOCKS_PROCESS_IN_SYNC_REPORT,
        );

        let mut batch: Vec<Block> = Vec::with_capacity(BATCH_SIZE);

        let cancellation_token = self.cancellation_token.clone();
        let sync_result = tokio::select! {
            () = cancellation_token.cancelled() => {
                log::debug!("Block sync cancelled");
                Err(anyhow::anyhow!("Sync cancelled"))
            }
            result = async {
                while let Some(block) = blocks_stream.next().await {
                    let block_number = block.number;
                    if self.cache.get_block_timestamp(block_number).is_some() {
                        continue;
                    }
                    batch.push(block);

                    // Process batch when full or last block
                    if batch.len() >= BATCH_SIZE || block_number >= to_block {
                        let batch_size = batch.len();

                        self.cache.add_blocks_batch(batch, use_copy_command).await?;
                        metrics.update(batch_size);

                        // Re-initialize batch vector
                        batch = Vec::with_capacity(BATCH_SIZE);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Treat this error as expected cancellation: filter/match for it and log rather than alert.
  2. Keep the client connected until sync_blocks completes if the cache must be fully populated.
  3. Re-run sync_blocks_checked after reconnect — cached block timestamps are skipped via cache lookups, so it resumes cheaply.
  4. If cancellations are unwanted, audit what is triggering the cancellation token (disconnect paths or task restarts).

Example fix

// before
client.sync_blocks_checked(chain, from, to).await?;

// after
if let Err(e) = client.sync_blocks_checked(chain, from, to).await {
    if e.to_string() == "Sync cancelled" {
        log::info!("block sync cancelled by shutdown");
    } else {
        return Err(e);
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

match client.sync_blocks_checked(chain, from, to).await {
    Ok(()) => {},
    Err(e) if e.to_string() == "Sync cancelled" => log::info!("sync cancelled, will resume"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Any cancellation of the token while a sync_blocks stream is active: calling disconnect(), starting a new task generation, or dropping the client mid-sync.

Common situations: User-initiated disconnect during a long historical block backfill; reconnect cycles cancelling an in-progress sync; service shutdown while bootstrapping block timestamps.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/785121ea46625425. Report an issue: GitHub.