nautechsystems/nautilus_trader · error · anyhow::Error
Persisted swap intent has no input amount
Error message
Persisted swap intent has no input amount
What it means
restore_swap_plan parses the persisted amount_in (as a U256 decimal string) to reconstruct the swap's input amount. If the intent row has no amount_in value, the swap plan cannot be rebuilt and reconciliation fails. The library requires this field because re-driving the swap needs the exact input amount that was originally prepared.
Solutions
- Mark the intent recoverable or remove it so reconciliation skips it; the plan cannot be restored without the amount.
- Backfill amount_in on the row from original trade/swap records if reconciliation of that swap is required.
- Confirm intent.schema_version matches EXECUTION_SCHEMA_VERSION and run any required migration before reuse.
- Always create intents through the adapter so all required columns are populated atomically.
Defensive patterns
Strategy: validation
Validate before calling
if intent.amount_in.is_none() {
eprintln!("intent {} lacks amount_in; swap cannot be rebuilt - backfill or mark recoverable", intent.id);
} Type guard
fn has_amount_in(intent: &ExecutionIntentRow) -> bool {
intent.amount_in.as_deref().map(|s| !s.is_empty()).unwrap_or(false)
} Try / catch
// Treat a missing amount as unrecoverable and quarantine the row
match restore_swap_plan(&intent) {
Err(e) if e.to_string().contains("no input amount") => mark_intent_recoverable(&intent),
Err(e) => return Err(e),
Ok(plan) => drive(plan),
} Prevention
- Apply schema migrations before reusing an older durable store
- Let writes of intent rows complete atomically (adapter-managed inserts only)
- Keep adapter and database schema versions in lockstep
- Backfill missing columns from trade records before reconnect
When it happens
Trigger: reconcile_unresolved_execution (from connect) or build_execution_verification_migration encounters an active ExecutionIntentRow with intent.amount_in = None when calling restore_swap_plan. Occurs with rows from older schema versions, partial writes, or manual DB manipulation.
Common situations: Adapter upgrade from a schema that did not persist amount_in while old rows remain; an interrupted insert when the intent was created; manual database cleanup dropping the value; running a mismatched adapter version against an old database.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Persisted swap intent has no pool address
- Persisted pool does not match restored pool
- Restored pool has no fee
- Cannot persist position event with no position_id
- Contract address should be set in logs
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/d5f1be825f654eea.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/blockchain/src/execution/client.rs:1078
);
let pool = self.resolve_pool(&instrument_id)?;
let pool_address = Address::from_str(
intent
.pool_address
.as_deref()
.ok_or_else(|| anyhow::anyhow!("Persisted swap intent has no pool address"))?,
)?;
anyhow::ensure!(
pool.address == pool_address,
"Persisted pool {pool_address} does not match restored pool {}",
pool.address
);
let amount_in = U256::from_str(
intent
.amount_in
.as_deref()
.ok_or_else(|| anyhow::anyhow!("Persisted swap intent has no input amount"))?,
)?;
let fee = U24::try_from(
pool.fee
.ok_or_else(|| anyhow::anyhow!("Restored pool {instrument_id} has no fee"))?,
)?;
let quote_token = pool.get_quote_token();
let quote_currency = Currency::new_checked(
"e_token.symbol,
quote_token.decimals,
0,
"e_token.name,
CurrencyType::Crypto,
)?;
let (token_in, token_out) = swap_token_pair(
order.order_side(),
pool.get_base_token().address,
quote_token.address,
)?;View on GitHub (pinned to 18893faf8b)