{"record":{"id":"e9c9bbcfa4d89848","repo":"nautechsystems/nautilus_trader","slug":"execution-intent-intent-id-is-not-recoverable-fr","errorCode":null,"errorMessage":"Execution intent {intent_id} is not recoverable from preparation","messagePattern":"Execution intent (.+?) is not recoverable from preparation","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6537,"sourceCode":"        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to lock recoverable execution intent: {e}\"))?\n        .ok_or_else(|| anyhow::anyhow!(\"Execution intent {intent_id} was not found\"))?;\n        anyhow::ensure!(\n            current_status == \"prepared\",\n            \"Execution intent {intent_id} is {current_status}, not recoverable before signing\"\n        );\n        let result = sqlx::query(\n            \"\n            UPDATE execution_intent\n            SET status = 'recoverable', active = FALSE, updated_at = NOW()\n            WHERE id = $1 AND status = 'prepared'\n            \",\n        )\n        .bind(intent_id)\n        .execute(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to mark execution intent recoverable: {e}\"))?;\n        anyhow::ensure!(\n            result.rows_affected() == 1,\n            \"Execution intent {intent_id} is not recoverable from preparation\"\n        );\n        sqlx::query(\n            \"\n            INSERT INTO execution_transaction_transition (\n                intent_id, transition_key, from_status, to_status\n            ) VALUES ($1, 'recoverable', $2, 'recoverable')\n            ON CONFLICT (intent_id, transition_key) DO NOTHING\n            \",\n        )\n        .bind(intent_id)\n        .bind(current_status)\n        .execute(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to record recoverable transition: {e}\"))?;\n        transaction\n            .commit()","sourceCodeStart":6519,"sourceCodeEnd":6555,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6519-L6555","documentation":"The conditional UPDATE (`WHERE id = $1 AND status = 'prepared'`) affected 0 rows even though the earlier SELECT FOR UPDATE read the row, so anyhow::ensure! raises \"... is not recoverable from preparation\". Since the row is FOR UPDATE-locked inside the same transaction, status should not have changed; 0 rows usually signals a logic/data anomaly rather than a race.","triggerScenarios":"Calling mark_execution_intent_recoverable on an intent whose UPDATE matched no row — in practice only possible via data corruption, a modified/inconsistent schema (e.g. status column collation or case mismatch), triggers altering the row, or reading through a different snapshot/replica setup than the UPDATE writes to.","commonSituations":"A database trigger or middleware rewrote the status during the transaction; schema drift (status stored with different casing such as 'PREPARED'); someone patched the UPDATE predicate; exotic replication routing where the lock and update hit divergent views.","solutions":["Inspect the row directly: SELECT id, status FROM execution_intent WHERE id = $1; confirm the status value matches 'prepared' byte-for-byte (casing, whitespace).","Check for triggers or row-level security policies on execution_intent that could alter visibility or the status between SELECT and UPDATE.","Verify no schema drift: the code expects a plain lowercase 'prepared' string in the status column.","If it persists, treat as an internal invariant violation and file a bug with the transition audit log from execution_transaction_transition."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let status: String = sqlx::query_scalar(\"SELECT status FROM execution_intent WHERE id = $1\")\n    .bind(intent_id)\n    .fetch_one(pool)\n    .await?;\nassert_eq!(status, \"prepared\", \"unexpected status encoding: {status:?}\");","typeGuard":null,"tryCatchPattern":"// Should be impossible under normal operation; log loudly and halt recovery\nmatch db.mark_execution_intent_recoverable(id).await {\n    Err(e) if e.to_string().contains(\"not recoverable from preparation\") => {\n        tracing::error!(%id, \"invariant violation: locked row did not update\");\n        // escalate / page, inspect triggers and schema\n    }\n    other => other?,\n}","preventionTips":["Ensure status values are stored as exact lowercase strings ('prepared').","Audit for triggers or RLS policies on execution_intent that mutate or filter rows.","Pin schema migrations to adapter expectations; verify with integration tests.","Alert on this error — it indicates data or logic corruption, not transient failure."],"tags":["database","internal-invariant","sql"],"backgroundTag":"internal-invariant-violation","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"}