nautechsystems/nautilus_trader · error · anyhow::Error
Execution payload rewrap left {remaining} row(s)
Error message
Execution payload rewrap left {remaining} row(s) What it means
When a rewrap batch finds no more rows to process, the code locks the table and runs a verification COUNT of rows that still have `payload_expected` with a NULL/missing plaintext constraint violation: raw_transaction set, sealed_transaction NULL, or an envelope not sealed under the new active key. If any rows remain (`remaining != 0`), the rewrap is not actually complete and this error aborts the state transition to 'ready'. It is a safety check that the batch loop truly finished the rewrap.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5470
.await
.context("failed to load execution payload rewrap batch")?;
if rows.is_empty() {
sqlx::query("LOCK TABLE execution_transaction_hash IN SHARE ROW EXCLUSIVE MODE")
.execute(&mut *transaction)
.await
.context("failed to lock execution payload rewrap completion")?;
let remaining = sqlx::query_scalar::<_, i64>(
"SELECT COUNT(*) FROM execution_transaction_hash \
WHERE payload_expected \
AND (raw_transaction IS NOT NULL OR sealed_transaction IS NULL \
OR substring(sealed_transaction FROM 2 FOR 32) <> $1)",
)
.bind(keys.active_key_id().as_slice())
.fetch_one(&mut *transaction)
.await
.context("failed to verify execution payload rewrap completion")?;
anyhow::ensure!(
remaining == 0,
"Execution payload rewrap left {remaining} row(s)"
);
sqlx::query(
"UPDATE execution_payload_state SET operation = 'ready', updated_at = NOW() \
WHERE component = 'signed_transactions' AND operation = 'rewrap'",
)
.execute(&mut *transaction)
.await
.context("failed to mark execution payload rewrap complete")?;
transaction
.commit()
.await
.context("failed to commit execution payload rewrap completion")?;
return Ok(true);
}
for hash in rows {View on GitHub (pinned to 18893faf8b)
Solutions
- Identify the leftover rows: `SELECT id FROM execution_transaction_hash WHERE payload_expected AND (raw_transaction IS NOT NULL OR sealed_transaction IS NULL OR substring(sealed_transaction FROM 2 FOR 32) <> $1)`.
- Stop all writers to `execution_transaction_hash` (adapter instances, ETL jobs) and re-run the rewrap so the batch loop can process the remaining rows.
- Fix half-migrated rows: either re-seal them under the active key (restore from a source with plaintext + re-seal) or correct `payload_expected` if these rows should not carry payloads.
- Check for concurrent insert paths and ensure they seal payloads with the current active key before the rewrap declares completion.
Defensive patterns
Strategy: retry
Validate before calling
let remaining: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM execution_transaction_hash WHERE payload_expected AND (raw_transaction IS NOT NULL OR sealed_transaction IS NULL OR substring(sealed_transaction FROM 2 FOR 32) <> $1)")
.bind(active_key_id.as_slice()).fetch_one(&mut *tx).await?;
// only declare the rewrap complete when remaining == 0 Try / catch
match rewrap_result {
Ok(done) if done => {/* rewrap complete */},
Err(e) if e.to_string().contains("Execution payload rewrap left") => {
// quiesce writers, then retry the batch loop after fixing/remediating rows
},
Err(e) => return Err(e),
} Prevention
- Quiesce all writers to `execution_transaction_hash` for the duration of a rewrap.
- Monitor the leftover-row COUNT during maintenance and alert if it stops decreasing.
- Ensure all insert paths seal payloads with the current active key before marking `payload_expected`.
- After any restore/backfill, run a full rewrap pass before declaring the storage 'ready'.
When it happens
Trigger: Rows exist that the batch SELECT (which filters on `substring(sealed_transaction FROM 2 FOR 32) <> $1` only for non-null envelopes) skipped but the stricter verification COUNT flags — e.g. rows with `payload_expected=true` but `sealed_transaction IS NULL` or `raw_transaction IS NOT NULL` (plaintext rows), or rows inserted concurrently between the last batch and the table lock on a table where the lock was not held by a competing writer.
Common situations: A writer inserted new signed-transaction rows with plaintext `raw_transaction` while a rewrap was running; a previous failed rewrap left rows half-migrated (NULL envelopes); manual data loads backfilled `payload_expected` rows without sealed envelopes; trigger/ETL jobs writing to `execution_transaction_hash` during maintenance.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Execution payload rewrap state is missing
- Execution payload {} has no envelope during rewrap
- Execution payload {} contains plaintext during rewrap
- Replacement hash {transaction_hash} conflicts with another i
- Execution payload storage is in {operation} maintenance; com
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/0349e7c136973899.
Report an issue: GitHub.