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
- Rotate the active payload key to a fresh key, then retry the operation
- Lower per-node seal usage or add monitoring/alerting on seal counters to rotate before the cap
- 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
- Alert on seal counters approaching EXECUTION_PAYLOAD_MAX_SEALS
- Schedule proactive key rotation before the cap is reached
- Capacity-plan seal volume against the configured limit
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
- Execution payload storage is in {operation} maintenance; com
- Execution payload storage is in {operation} maintenance, not
- Current database payload key is not configured for rewrap
- Configured active payload key does not match the database ac
- Implement FromRow for FuturesSpread
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ac4873be273a15ce.
Report an issue: GitHub.