nautechsystems/nautilus_trader · error

Replacement transaction {transaction_hash} is not authentica

Error message

Replacement transaction {transaction_hash} is not authenticated

What it means

After locking the `execution_transaction_hash` row, the code checks its `payload_expected` flag. If false, the row exists but its payload is not expected/authenticated (e.g. it was recorded without an authenticated payload or was previously retired), so treating this transaction as a verified replacement is refused.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:6445

                    "
                    SELECT id, payload_expected, current
                    FROM execution_transaction_hash
                    WHERE intent_id = $1 AND chain_id = $2 AND transaction_hash = $3
                    FOR UPDATE
                    ",
                )
                .bind(scan.intent_id)
                .bind(chain_id)
                .bind(transaction_hash)
                .fetch_optional(&mut *transaction)
                .await
                .context("failed to lock authenticated replacement payload")?
                .ok_or_else(|| {
                    anyhow::anyhow!(
                        "Replacement transaction {transaction_hash} has no retained payload"
                    )
                })?;
            anyhow::ensure!(
                payload_expected,
                "Replacement transaction {transaction_hash} is not authenticated"
            );

            if !already_current {
                sqlx::query(
                    "
                    UPDATE execution_transaction_hash
                    SET current = FALSE, status = 'replaced', updated_at = NOW()
                    WHERE intent_id = $1 AND current
                    ",
                )
                .bind(scan.intent_id)
                .execute(&mut *transaction)
                .await
                .context("failed to retire the prior transaction hash")?;
                sqlx::query(
                    "

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-run the payload authentication step for the transaction so `payload_expected` becomes TRUE before recording the scan.
  2. Only mark replacements whose payloads passed authentication; filter out unauthenticated hashes when building the scan.
  3. Recreate the `execution_transaction_hash` row through the authenticated retention path if the flag was cleared incorrectly.

Example fix

// before: accepting any stored hash
let scan = build_scan(stored_hashes);
// after: only authenticated ones
let scan = build_scan(stored_hashes.into_iter().filter(|h| h.payload_expected));
Defensive patterns

Strategy: validation

Validate before calling

let (payload_expected, current) = sqlx::query_as::<_, (bool, bool)>("SELECT payload_expected, current FROM execution_transaction_hash WHERE intent_id=$1 AND chain_id=$2 AND transaction_hash=$3").bind(intent_id).bind(chain_id).bind(tx_hash).fetch_one(&pool).await?;
if !payload_expected { return Err(anyhow!("tx {tx_hash} lacks an authenticated payload")); }

Type guard

fn payload_is_authenticated(row: &(i64, bool, bool)) -> bool { row.1 }

Prevention

When it happens

Trigger: Attaching `matched_transaction_hash` where the stored row has `payload_expected = FALSE` — i.e. the hash was recorded but never backed by an authenticated payload, or its expected-payload flag was cleared.

Common situations: The payload authentication step failed or was skipped earlier in the pipeline; the row was written by a legacy/unauthenticated path; flags were mutated by a concurrent transition between retention and the scan record.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/297bb2a8387aff0c. Report an issue: GitHub.