nautechsystems/nautilus_trader · error · anyhow::Error

Execution payload key reached its seal limit; rotate the act

Error message

Execution payload key reached its seal limit; rotate the active key before continuing

What it means

Before consuming a payload nonce, the code checks that the key's seal counter still has room below EXECUTION_PAYLOAD_MAX_SEALS (the reservation query requires usage <= MAX_SEALS - 1). This error means the active key has been used for its maximum number of seals and must be rotated before any further payloads can be sealed.

Source

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

    Ok(())
}

async fn reserve_execution_payload_seal(
    transaction: &mut Transaction<'_, Postgres>,
    key_id: &[u8; 32],
) -> anyhow::Result<()> {
    let seals = sqlx::query_scalar::<_, i64>(
        "INSERT INTO execution_payload_key_state (key_id, seals) VALUES ($1, 1) \
         ON CONFLICT (key_id) DO UPDATE SET seals = execution_payload_key_state.seals + 1 \
         WHERE execution_payload_key_state.seals < $2 \
         RETURNING seals",
    )
    .bind(key_id.as_slice())
    .bind(EXECUTION_PAYLOAD_MAX_SEALS - 1)
    .fetch_optional(&mut **transaction)
    .await
    .context("failed to reserve execution payload nonce use")?;
    anyhow::ensure!(
        seals.is_some(),
        "Execution payload key reached its seal limit; rotate the active key before continuing"
    );
    Ok(())
}

async fn validate_execution_payload_key_inventory(
    transaction: &mut Transaction<'_, Postgres>,
    keys: &PayloadKeySet,
) -> anyhow::Result<()> {
    let envelopes = sqlx::query_scalar::<_, Vec<u8>>(
        "SELECT DISTINCT substring(sealed_transaction FROM 1 FOR 33) \
         FROM execution_transaction_hash \
         WHERE sealed_transaction IS NOT NULL",
    )
    .fetch_all(&mut **transaction)
    .await
    .context("failed to inspect execution payload key inventory")?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Rotate the active payload key to a fresh key, then retry the operation
  2. Lower per-node seal usage or add monitoring/alerting on seal counters to rotate before the cap
  3. Verify EXECUTION_PAYLOAD_MAX_SEALS is configured as intended if the limit seems unexpectedly low
Defensive patterns

Strategy: validation

Validate before calling

let seals_used: i64 = get_key_seal_count(active_key_id).await?;
if seals_used >= EXECUTION_PAYLOAD_MAX_SEALS - 1 {
    rotate_key().await?;
}

Try / catch

match seal_with_key(key_id).await {
    Ok(nonce) => proceed(nonce),
    Err(e) if e.to_string().contains("seal limit") => rotate_key_and_retry().await,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Attempting to reserve a nonce/seal with an execution payload key whose cumulative seal count has reached EXECUTION_PAYLOAD_MAX_SEALS.

Common situations: Long-lived deployments that never rotated keys hitting the seal cap; forgetting to schedule key rotation; burst activity exhausting the key's allowed seals faster than expected.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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