nautechsystems/nautilus_trader · error · anyhow::Error

Profiler watermark log did not come from expected pool {pool

Error message

Profiler watermark log did not come from expected pool {pool_address}

What it means

The profiler watermark log must be emitted by the expected pool contract address. The library compares the log's address field against pool_address and throws if they differ, ensuring the watermark event actually originated from the pool being profiled rather than some other contract.

Source

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

        matching_logs.len(),
        position.log_index
    );
    let log = matching_logs[0];
    let log_transaction_hash = B256::from_str(&rpc_log::extract_transaction_hash(log)?)
        .with_context(|| "Invalid profiler log transaction hash")?;
    let log_block_hash = log
        .block_hash
        .as_deref()
        .ok_or_else(|| anyhow::anyhow!("Profiler log has no block hash"))?;
    anyhow::ensure!(
        !log.removed
            && log_transaction_hash == transaction_hash
            && rpc_log::extract_block_number(log)? == position.number
            && rpc_log::extract_transaction_index(log)? == position.transaction_index
            && B256::from_str(log_block_hash)? == expected_block_hash,
        "Profiler log position does not match its ingestion watermark"
    );
    anyhow::ensure!(
        rpc_log::extract_address(log)? == pool_address,
        "Profiler watermark log did not come from expected pool {pool_address}"
    );
    let signature = log
        .topics
        .first()
        .ok_or_else(|| anyhow::anyhow!("Profiler watermark log has no event signature"))?;
    let supported =
        profiler_event_signatures(pool).any(|expected| expected.eq_ignore_ascii_case(signature));
    anyhow::ensure!(
        supported,
        "Profiler watermark log has an unsupported event signature"
    );
    Ok(())
}

fn profiler_event_signatures(pool: &Pool) -> impl Iterator<Item = &str> {
    [

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify pool_address in the profiler configuration matches the contract that actually emits the event (check the log's address field in a block explorer).
  2. If a pool was upgraded/migrated, update the configured address and re-ingest the watermark.
  3. Ensure the watermark log_index points at the correct log within the transaction, not a sibling pool's event.
  4. For proxy contracts, configure the proxy address that emits events, not the implementation address.

Example fix

// before
profiler.configure(pool_address: impl_addr);

// after: use the emitting (proxy) address
profiler.configure(pool_address: proxy_addr);
Defensive patterns

Strategy: validation

Validate before calling

let addr = extract_address(&log)?;
if !addr.eq_ignore_ascii_case(pool_address) {
    return Err(format!("log from {addr}, expected {pool_address}").into());
}

Try / catch

match client.validate_watermark_pool(&log, pool_address) {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("did not come from expected pool") => reload_pool_config_and_retry(e),
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Profiler validation where rpc_log::extract_address(log) != pool_address — i.e., the log at the watermark index was emitted by a different contract than the configured pool.

Common situations: Pool address misconfiguration (wrong token pair, proxy vs implementation address); a pool migration/upgrade changed the emitting contract; the transaction touched multiple pools and the watermark index points at another pool's event.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — 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/9782eeb5d1156597. Report an issue: GitHub.