nautechsystems/nautilus_trader · error · anyhow::Error

Allowed token pair {token_in} -> {token_out} is not fully pi

Error message

Allowed token pair {token_in} -> {token_out} is not fully pinned by the deployment manifest

What it means

During manifest validation, every configured allowed token pair must have both tokens defined in the deployment manifest's token registry (token_decimals). If either address is missing from that registry, the pair cannot be trusted for swaps and validation fails. This ensures swap routing only uses tokens explicitly pinned by the deployment manifest.

Source

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

        anyhow::ensure!(
            token_decimals.contains_key(&weth),
            "Deployment manifest has no wrapped native token identity"
        );

        if let Some(tokens) = &config.tokens {
            for token in tokens {
                let address = validate_address(token)?;
                anyhow::ensure!(
                    token_decimals.contains_key(&address),
                    "Configured token {address} has no deployment manifest identity"
                );
            }
        }

        for (token_in, token_out) in config.allowed_token_pairs.as_deref().unwrap_or_default() {
            let token_in = validate_address(token_in)?;
            let token_out = validate_address(token_out)?;
            anyhow::ensure!(
                token_in != token_out
                    && token_decimals.contains_key(&token_in)
                    && token_decimals.contains_key(&token_out),
                "Allowed token pair {token_in} -> {token_out} is not fully pinned by the deployment manifest"
            );
        }

        for limit in config.quote_spend_limits.as_deref().unwrap_or_default() {
            let spend_token = validate_address(&limit.spend_token)?;
            anyhow::ensure!(
                token_decimals.get(&spend_token) == Some(&limit.spend_token_decimals),
                "Quote spend limit decimals do not match the deployment manifest"
            );
        }

        let pool_contracts = role_addresses(BlockchainContractRole::Pool)?;

        for pool in &manifest.pools {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add the missing token address (with correct decimals) to the deployment manifest's token registry
  2. Remove or correct the token pair in config.allowed_token_pairs
  3. Verify addresses are parsed consistently (same checksum format) via validate_address

Example fix

// before
allowed_token_pairs = [["0xAAA...", "0xBBB..."]] // 0xBBB... not in manifest tokens
// after
# manifest.toml
[[tokens]]
address = "0xBBB..."
decimals = 6
Defensive patterns

Strategy: validation

Validate before calling

// before calling preflight
for (tin, tout) in &config.allowed_token_pairs {
    assert!(manifest.tokens.iter().any(|t| &t.address == tin), "token_in {} not in manifest", tin);
    assert!(manifest.tokens.iter().any(|t| &t.address == tout), "token_out {} not in manifest", tout);
}

Prevention

When it happens

Trigger: Calling preflight/validate_manifest_contracts with config.allowed_token_pairs containing a token address that is not present in the manifest's token_decimals map, or a pair where token_in equals token_out.

Common situations: Operator adds a new trading pair to allowed_token_pairs but forgets to add the token (with decimals) to the deployment manifest; typo'd or checksummed-vs-lowercase address mismatch; manifest updated on another deployment.

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/ca8ec006ead64870. Report an issue: GitHub.