{"record":{"id":"92c65125dbd532e8","repo":"nautechsystems/nautilus_trader","slug":"cannot-safely-migrate-unresolved-legacy-unresolv","errorCode":null,"errorMessage":"Cannot safely migrate {unresolved_legacy} unresolved execution schema version 1 transaction(s); resolve them with the prior version before enabling version {EXECUTION_SCHEMA_VERSION}","messagePattern":"Cannot safely migrate (.+?) unresolved execution schema version 1 transaction\\(s\\); resolve them with the prior version before enabling version (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":3616,"sourceCode":"            && installed_version > EXECUTION_SCHEMA_VERSION\n        {\n            anyhow::bail!(\n                \"Execution schema version {} is newer than supported version {EXECUTION_SCHEMA_VERSION}\",\n                installed_version\n            );\n        }\n\n        let unresolved_legacy = sqlx::query_scalar::<_, i64>(\n            \"\n            SELECT COUNT(*)\n            FROM execution_transaction\n            WHERE status IN ('pending', 'included', 'reverted')\n            \",\n        )\n        .fetch_one(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to inspect legacy execution transactions: {e}\"))?;\n        anyhow::ensure!(\n            unresolved_legacy == 0,\n            \"Cannot safely migrate {unresolved_legacy} unresolved execution schema version 1 transaction(s); resolve them with the prior version before enabling version {EXECUTION_SCHEMA_VERSION}\"\n        );\n\n        for statement in [\n            \"\n            CREATE OR REPLACE FUNCTION execution_transaction_v2_fence()\n            RETURNS TRIGGER AS $$\n            BEGIN\n                RAISE EXCEPTION 'Legacy execution writer refused after schema version 2 activation';\n            END;\n            $$ LANGUAGE plpgsql\n            \",\n            \"DROP TRIGGER IF EXISTS execution_transaction_v2_fence ON execution_transaction\",\n            \"\n            CREATE TRIGGER execution_transaction_v2_fence\n            BEFORE INSERT OR UPDATE OR DELETE ON execution_transaction\n            FOR EACH STATEMENT EXECUTE FUNCTION execution_transaction_v2_fence()","sourceCodeStart":3598,"sourceCodeEnd":3634,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L3598-L3634","documentation":"This error is raised by the execution schema migration in database.rs:3616 before the v1 -> v2 schema upgrade runs. The migration inspects the legacy execution_transaction table and refuses to proceed when transactions in the non-terminal statuses ('pending', 'included', 'reverted') remain, because v2 installs a fence trigger that permanently blocks writes to that legacy table. The library requires all legacy version-1 transactions to be resolved (moved to a terminal status) with the prior schema before the new schema is enabled.","triggerScenarios":"Calling the schema migration routine when SELECT COUNT(*) FROM execution_transaction WHERE status IN ('pending','included','reverted') returns a count greater than 0, i.e. unresolved legacy transactions exist at migration time.","commonSituations":"Upgrading a node to a build that activates EXECUTION_SCHEMA_VERSION 2 while the legacy writer still has in-flight pending/included/reverted rows; a crash or shutdown left legacy transactions unresolved; running the migration manually against a database that was actively written by the old version.","solutions":["Resolve all pending/included/reverted rows in execution_transaction to a terminal status using the prior (version 1) software before upgrading.","Shut down the old-version node cleanly and let it drain all in-flight transactions before starting the migration.","Query SELECT COUNT(*) FROM execution_transaction WHERE status IN ('pending','included','reverted') beforehand and only run the migration when it returns 0.","If rows are stale/orphaned (e.g. from a decommissioned wallet), clean them up with the prior version rather than editing the table directly."],"exampleFix":"-- before (migration fails)\nSELECT COUNT(*) FROM execution_transaction WHERE status IN ('pending','included','reverted');\n-- e.g. 3 rows\n\n-- after (resolve them with v1 first, e.g. mark dropped/replaced via the prior version)\nSELECT COUNT(*) FROM execution_transaction WHERE status IN ('pending','included','reverted');\n-- 0 rows, then run the v2 migration","handlingStrategy":"validation","validationCode":"let unresolved: i64 = sqlx::query_scalar(\n    \"SELECT COUNT(*) FROM execution_transaction WHERE status IN ('pending','included','reverted')\",\n).fetch_one(&pool).await?;\nif unresolved != 0 {\n    anyhow::bail!(\"{unresolved} unresolved legacy transactions; drain before v2 migration\");\n}","typeGuard":null,"tryCatchPattern":"match migrate_execution_schema(&pool).await {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"unresolved execution schema version 1\") => {\n        // drain legacy transactions with v1, then retry\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Drain all in-flight legacy transactions before scheduling a schema upgrade","Stop the old-version writer before running migrations","Pre-check pending transaction counts as part of the upgrade runbook"],"tags":["database","migration","schema","postgresql"],"backgroundTag":"schema-validation-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}