{"record":{"id":"dd1ed01e412100ea","repo":"nautechsystems/nautilus_trader","slug":"execution-intent-intent-id-is-current-status-dd1ed0","errorCode":null,"errorMessage":"Execution intent {intent_id} is {current_status}, not prepared for signing","messagePattern":"Execution intent (.+?) is (.+?), not prepared for signing","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6660,"sourceCode":"            )\n            .bind(EXECUTION_PAYLOAD_COMPONENT)\n            .fetch_one(&mut *transaction)\n            .await\n            .context(\"failed to inspect execution payload marker\")?;\n            anyhow::ensure!(\n                !marker,\n                \"Plaintext signed transaction persistence is disabled after payload activation\"\n            );\n        }\n        let current_status = sqlx::query_scalar::<_, String>(\n            \"SELECT status FROM execution_intent WHERE id = $1 FOR UPDATE\",\n        )\n        .bind(intent_id)\n        .fetch_optional(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to lock execution intent {intent_id}: {e}\"))?\n        .ok_or_else(|| anyhow::anyhow!(\"Execution intent {intent_id} was not found\"))?;\n        anyhow::ensure!(\n            current_status == TransactionStatus::Prepared.as_str()\n                || current_status == TransactionStatus::Signed.as_str(),\n            \"Execution intent {intent_id} is {current_status}, not prepared for signing\"\n        );\n\n        let row = sqlx::query_as::<_, ExecutionTransactionHashRow>(\n            \"\n            INSERT INTO execution_transaction_hash (\n                intent_id, chain_id, transaction_hash, payload_expected,\n                raw_transaction, sealed_transaction, status\n            )\n            SELECT id, chain_id, $3, TRUE, $4, $5, 'signed'\n            FROM execution_intent\n            WHERE id = $1 AND chain_id = $2 AND nonce IS NOT NULL\n            ON CONFLICT (chain_id, transaction_hash) DO UPDATE\n            SET transaction_hash = EXCLUDED.transaction_hash\n            WHERE execution_transaction_hash.intent_id = EXCLUDED.intent_id\n              AND execution_transaction_hash.payload_expected","sourceCodeStart":6642,"sourceCodeEnd":6678,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6642-L6678","documentation":"The execution intent exists but its status is neither 'prepared' nor 'signed', so persisting a signed transaction is an invalid state transition. The library enforces a strict state machine: only prepared (or idempotently re-signed) intents may accept a signed payload.","triggerScenarios":"Calling add_execution_transaction on an intent whose status is e.g. 'broadcast', 'confirmed', 'failed', or 'expired'. Typically a duplicate signing attempt after the transaction was already broadcast, a replay of an old signing job, or a concurrent worker that advanced the status between fetch and persist.","commonSituations":"Re-running a signing batch after a crash where intents already advanced; two consumers processing the same intent queue message; retrying a job that previously succeeded and moved the intent to a later status.","solutions":["Fetch the intent's current status first and skip persistence if it is already past 'signed' (the work is done).","If the status is a terminal/failure state, create a new intent with a fresh nonce instead of re-signing.","Resolve concurrent workers (single-owner queue) so only one actor advances an intent.","If status is stale due to a crashed prior run, reconcile the intent via the observation/recovery path rather than forcing a signed persist."],"exampleFix":"// before\ndb.add_execution_transaction(intent_id, ...).await?;\n// after\nlet status = db.intent_status(intent_id).await?;\nif status != \"prepared\" && status != \"signed\" { return Ok(()); /* already advanced */ }\ndb.add_execution_transaction(intent_id, ...).await?;","handlingStrategy":"validation","validationCode":"let status: String = sqlx::query_scalar(\"SELECT status FROM execution_intent WHERE id=$1\").bind(intent_id).fetch_one(&pool).await?;\nif !(status == \"prepared\" || status == \"signed\") {\n    tracing::info!(%intent_id, %status, \"intent already advanced; skipping signed persist\");\n    return Ok(SkipReason::AlreadyAdvanced);\n}","typeGuard":"fn signable_status(status: &str) -> bool { status == \"prepared\" || status == \"signed\" }","tryCatchPattern":"match db.add_execution_transaction(intent_id, ...).await {\n    Err(e) if e.to_string().contains(\"not prepared for signing\") => {\n        let status = fetch_status(intent_id).await?;\n        info!(%status, \"idempotent skip: intent already advanced\");\n    }\n    r => r?,\n}","preventionTips":["Check intent status before re-running any signing job after a crash","Make signing consumers idempotent: treat 'not prepared for signing' as skip-if-done","Ensure only one worker owns an intent at a time","Track status transitions and alert on unexpected regressions"],"tags":["database","state-machine","invalid-transition","transactions"],"backgroundTag":"invalid-state-transition","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}