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
- Re-run the payload authentication step for the transaction so `payload_expected` becomes TRUE before recording the scan.
- Only mark replacements whose payloads passed authentication; filter out unauthenticated hashes when building the scan.
- 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
- Run payload authentication before any scan that may match the transaction as a replacement.
- Filter candidate replacement hashes on `payload_expected = TRUE` when building scans.
- Re-authenticate rows whose payload flag was cleared before reusing them as replacements.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Verified finalized transaction count advanced without an act
- Verified finalized transaction count is outside the owned re
- Verified finalized header extension does not start at the du
- Finalized header ledger conflicts at height {}
- Verified nonce assignment manifest identity changed
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/297bb2a8387aff0c.
Report an issue: GitHub.