{"record":{"id":"bc01da7772078125","repo":"nautechsystems/nautilus_trader","slug":"execution-payload-changed-during-migration","errorCode":null,"errorMessage":"Execution payload {} changed during migration","messagePattern":"Execution payload (.+?) changed during migration","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":4987,"sourceCode":"            let unsealed = keys.unseal(&envelope, &context)?;\n            authenticate_retained_payload(&unsealed, &intent, &hash, keys.deployment_id())?;\n            anyhow::ensure!(\n                unsealed == raw_transaction,\n                \"Execution payload {} changed during seal round trip\",\n                hash.id\n            );\n            let result = sqlx::query(\n                \"UPDATE execution_transaction_hash \\\n                 SET sealed_transaction = $2, raw_transaction = NULL, updated_at = NOW() \\\n                 WHERE id = $1 AND raw_transaction = $3 AND sealed_transaction IS NULL\",\n            )\n            .bind(hash.id)\n            .bind(&envelope)\n            .bind(raw_transaction)\n            .execute(&mut *transaction)\n            .await\n            .context(\"failed to promote execution payload\")?;\n            anyhow::ensure!(\n                result.rows_affected() == 1,\n                \"Execution payload {} changed during migration\",\n                hash.id\n            );\n            sqlx::query(\n                \"UPDATE execution_payload_state SET progress_id = $1, updated_at = NOW() \\\n                 WHERE component = 'signed_transactions'\",\n            )\n            .bind(hash.id)\n            .execute(&mut *transaction)\n            .await\n            .context(\"failed to record execution payload migration progress\")?;\n        }\n\n        transaction\n            .commit()\n            .await\n            .context(\"failed to commit execution payload migration batch\")?;","sourceCodeStart":4969,"sourceCodeEnd":5005,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L4969-L5005","documentation":"This error is thrown by the execution payload migration path when an UPDATE intended to promote exactly one execution payload row affected zero rows. It means the row for the given hash id was changed or deleted concurrently between the SELECT and the UPDATE inside the transaction, so the migration aborted to avoid silently losing data.","triggerScenarios":"Calling the migration routine while another transaction deletes or rewrites the execution_payload row with the same hash id; running migration against a database where the expected row was already consumed by a prior partial migration; concurrent migration processes racing on the same hash.","commonSituations":"Two instances of the node/migrator running against the same database at once; a manual DBA cleanup removing rows mid-migration; re-running a failed migration whose transaction partially committed elsewhere.","solutions":["Ensure only one migrator process runs at a time (advisory lock or single-instance deployment)","Re-run the migration; it is transactional and aborted atomically, so state should be consistent","Check for concurrent writers/deleters on execution_payload tables and pause them during migration","Verify the hash id still exists before migrating"],"exampleFix":"// before: blind concurrent migration\nlet result = sqlx::query(\"UPDATE ... \").bind(hash.id).execute(&mut *transaction).await?;\nanyhow::ensure!(result.rows_affected() == 1, \"Execution payload {} changed during migration\", hash.id);\n// after: take an advisory lock first\nsqlx::query(\"SELECT pg_advisory_xact_lock($1)\").bind(MIGRATION_LOCK_KEY).execute(&mut *transaction).await?;","handlingStrategy":"try-catch","validationCode":"let exists: Option<i64> = sqlx::query_scalar(\"SELECT 1 FROM execution_payload WHERE id = $1\").bind(hash.id).fetch_optional(&mut conn).await?;\nif exists.is_none() { return Err(anyhow::anyhow!(\"payload {} absent before migration\", hash.id)); }","typeGuard":null,"tryCatchPattern":"match migrate_payload(&mut tx, hash).await {\n    Err(e) if e.to_string().contains(\"changed during migration\") => {\n        // serialize migrations: take advisory lock and retry once\n        sqlx::query(\"SELECT pg_advisory_xact_lock($1)\").bind(LOCK_KEY).execute(&mut tx).await?;\n        retry_migration(hash).await\n    }\n    other => other,\n}","preventionTips":["Run migrations single-instance or under an advisory lock","Never mutate execution payload tables outside the migration path","Re-run the transactional migration after an abort — it commits atomically","Monitor for concurrent writers during migration windows"],"tags":["database","migration","concurrency"],"backgroundTag":"database-write-failed","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"}