nautechsystems/nautilus_trader · error · anyhow::Error

Execution payload key reached its seal limit; rotate the…

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.

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.

Appendix: 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)