nautechsystems/nautilus_trader · error · anyhow::Error

Pool {} does not have exactly one deployment manifest defini

Error message

Pool {} does not have exactly one deployment manifest definition

What it means

`validate_manifest_pool` (crates/adapters/blockchain/src/execution/client.rs:4125) looks up the plan's pool address in the deployment manifest and requires exactly one matching pool entry, since it must resolve unique token0/token1/factory/quote-contract identities for the pool. Zero matches means the pool is not in the manifest; more than one means duplicate or conflicting definitions.

Source

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

            );
            Ok((
                quote.amount,
                derive_min_amount_out(base_amount, slippage_bps)?,
            ))
        }
    }
}

fn validate_manifest_pool(
    plan: &SwapPlan,
    manifest: &BlockchainDeploymentManifest,
) -> anyhow::Result<Address> {
    let matching = manifest
        .pools
        .iter()
        .filter(|pool| Address::from_str(&pool.address).ok() == Some(plan.pool_address))
        .collect::<Vec<_>>();
    anyhow::ensure!(
        matching.len() == 1,
        "Pool {} does not have exactly one deployment manifest definition",
        plan.pool_address
    );
    let pool = matching[0];
    let token0 = Address::from_str(&pool.token0)?;
    let token1 = Address::from_str(&pool.token1)?;
    let factory = Address::from_str(&pool.factory)?;
    let quote_contract = Address::from_str(&pool.quote_contract)?;
    anyhow::ensure!(
        token0 == plan.pool.token0.address
            && token1 == plan.pool.token1.address
            && pool.fee == plan.pool.fee.expect("validated pool fee")
            && factory == plan.factory,
        "Cached pool {} does not match its deployment manifest identity",
        plan.pool_address
    );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add a single definition for the pool (address, token0, token1, fee, factory, quote_contract) to the deployment manifest, or point the client at the manifest that contains it.
  2. Deduplicate the pools list in the manifest so each pool address appears exactly once.
  3. Confirm the pool address in the SwapPlan matches the manifest entry exactly (same address bytes).
  4. Regenerate/redeploy the manifest from source so it is in sync with the deployed pools.

Example fix

// before: duplicate pool entries in the manifest
"pools": [
  { "address": "0xabc...", "fee": 3000 },
  { "address": "0xabc...", "fee": 3000 }
]
// after: exactly one entry per pool address
"pools": [
  { "address": "0xabc...", "fee": 3000, "token0": "...", "token1": "...", "factory": "...", "quoteContract": "..." }
]
Defensive patterns

Strategy: validation

Validate before calling

let matches: Vec<_> = manifest.pools.iter()
    .filter(|p| Address::from_str(&p.address).ok() == Some(plan.pool_address))
    .collect();
if matches.len() != 1 { return Err("pool must appear exactly once in the manifest"); }

Type guard

fn has_unique_pool(manifest: &BlockchainDeploymentManifest, addr: Address) -> bool {
    manifest.pools.iter()
        .filter(|p| Address::from_str(&p.address).ok() == Some(addr))
        .count() == 1
}

Try / catch

match client.execute_swap(plan).await {
    Err(e) if e.to_string().contains("exactly one deployment manifest definition") => {
        // reload or repair the manifest, then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Executing a swap whose plan.pool_address does not appear in the deployment manifest's pools list (0 matches), or the manifest contains the same pool address twice or across multiple environments/chains (>=2 matches).

Common situations: Stale or incomplete deployment manifest missing a newly created pool; pool address typo or checksum/case variant handled differently; manifest merged from multiple deployment files duplicating a pool entry; running against a different environment's manifest than the one the pool was deployed in.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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