nautechsystems/nautilus_trader · error · anyhow::Error

Execution intent {} has unknown purpose {}

Error message

Execution intent {} has unknown purpose {}

What it means

During startup reconciliation, TransactionPurpose::parse() returned None for the intent's persisted purpose column. The parser only accepts the exact strings "wrap", "approve", and "swap" (WETH deposit, ERC-20 approve, Uniswap V3 exactInputSingle). Any other value means the row cannot be mapped to a known replay strategy, so reconciliation aborts.

Source

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

            return Ok(());
        };
        anyhow::ensure!(
            intent.schema_version == crate::execution::transaction::EXECUTION_SCHEMA_VERSION,
            "Execution intent {} uses unsupported schema version {}",
            intent.id,
            intent.schema_version
        );

        if matches!(intent.status.as_str(), "prepared" | "signed") {
            database
                .mark_execution_intent_recoverable(intent.id)
                .await?;
            release_preparing_slot(&self.in_flight);
            return Ok(());
        }

        let purpose = TransactionPurpose::parse(&intent.purpose).ok_or_else(|| {
            anyhow::anyhow!(
                "Execution intent {} has unknown purpose {}",
                intent.id,
                intent.purpose
            )
        })?;
        let nonce = intent
            .nonce
            .ok_or_else(|| anyhow::anyhow!("Active execution intent {} has no nonce", intent.id))?;
        let hashes = database.get_execution_transaction_hashes(intent.id).await?;
        let current = current_execution_hash(intent.id, &hashes)?;
        let tx_hash = B256::from_str(&current.transaction_hash).with_context(|| {
            format!(
                "Execution intent {} has invalid transaction hash {}",
                intent.id, current.transaction_hash
            )
        })?;
        *self.in_flight.lock().expect("in-flight mutex poisoned") =
            Some(InFlightSlot::AwaitingFinality(InFlightTransaction {

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Inspect the row: SELECT id, purpose, status FROM execution_intent WHERE active AND purpose NOT IN ('wrap','approve','swap');
  2. If the intent is genuinely one of the known purposes, correct the string (lowercase, no whitespace)
  3. If it was written by a newer version, reconcile with that version or archive the row (set active = FALSE) after confirming no funds are at risk
  4. Restrict write access to the execution tables so only the NautilusTrader process mutates them

Example fix

-- before
SELECT id, purpose FROM execution_intent WHERE active;
-- purpose = 'Swap ' (trailing space, parse fails)

-- after
UPDATE execution_intent SET purpose = 'swap' WHERE id = 42;
Defensive patterns

Strategy: validation

Validate before calling

-- Verify persisted purposes are parseable before startup
SELECT id, purpose
FROM execution_intent
WHERE active
  AND purpose NOT IN ('wrap', 'approve', 'swap');
-- Expect zero rows before calling connect().

Type guard

def is_known_purpose(value: str) -> bool:
    return value in ('wrap', 'approve', 'swap')

Try / catch

try:
    client.connect()
except Exception as e:
    if 'unknown purpose' in str(e):
        # inspect/repair execution_intent.purpose, then retry connect
        ...
    raise

Prevention

When it happens

Trigger: An active execution_intent row whose purpose is not exactly 'wrap', 'approve', or 'swap': manually edited rows, a newer binary that persisted an additional purpose enum variant, or data written into the table by another tool.

Common situations: Hand-fixing rows in Postgres during incident response; running a newer adapter version against a store read by an older one; typos introduced by ETL jobs touching the execution_intent table.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/ca6b3a33baa03402. Report an issue: GitHub.