nautechsystems/nautilus_trader · error · anyhow::Error

Swap decision header conflicts with profiler ancestry

Error message

Swap decision header conflicts with profiler ancestry

What it means

After fetching the canonical decision header, the library walks the header ancestry from the profiler watermark block to the decision head via verify_header_window. The last ancestry header must be exactly the decision head; otherwise the verified window disagrees with the header used for the swap decision, indicating a reorg or inconsistent verification results. Execution is aborted.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:3999

        validate_profiler_event_verified(
            position,
            expected_block_hash,
            plan.pool_address,
            &plan.pool,
            verification,
        )
        .await?;
    }

    let ancestry = verified_value(
        verification
            .verify_header_window(canonical_block, head.number)
            .await,
        "profiler-to-decision ancestry",
    )?;

    if let Some(last) = ancestry.last() {
        anyhow::ensure!(
            *last == head,
            "Swap decision header conflicts with profiler ancestry"
        );
    } else {
        anyhow::ensure!(
            canonical_block == head,
            "Empty profiler ancestry does not end at the decision header"
        );
    }
    verified_value(
        verification
            .verify_deployment_manifest(manifest, head.number)
            .await,
        "swap deployment manifest",
    )?;
    let quote_contract = validate_manifest_pool(plan, manifest)?;
    let quote_kind = match plan.order.order_side() {
        OrderSide::Sell => SwapQuoteKind::ExactInput(quantity_to_raw_amount(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Retry execution after the reorg settles; the window is re-verified each attempt.
  2. Pin RPC/verification traffic to a single consistent endpoint instead of load-balanced forks.
  3. Refresh the profiler watermark and re-derive both the canonical block and head.
  4. Increase confirmation depth (max_age_blocks / head lag) so decisions are not made near reorg-prone tips.
Defensive patterns

Strategy: retry

Validate before calling

let ancestry = verification.verify_header_window(canonical_block, head.number).await?;
if let Some(last) = ancestry.last() {
    if *last != head {
        anyhow::bail!("decision head not on verified ancestry; reorg in progress");
    }
}

Try / catch

match build_quote_anchors(...).await {
    Ok(a) => execute(a),
    Err(e) if e.to_string().contains("conflicts with profiler ancestry") => {
        tokio::time::sleep(backoff).await; // let the reorg settle
        retry();
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: During swap quote validation, verify_header_window(canonical_block, head.number) returns a non-empty ancestry whose last element != head — i.e. the chain reorganized between the ancestry fetch and the head fetch, or two verification calls hit different forks.

Common situations: Active reorgs on the target chain during execution; load-balanced RPC endpoints serving different forks; verifier cache out of sync; attempting execution during unstable network conditions right after a chain reorganization event.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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