nautechsystems/nautilus_trader · error
Persisted signed transaction is not EIP-1559
Error message
Persisted signed transaction is not EIP-1559
What it means
decode_signed_transaction decodes a persisted raw signed transaction and only supports EIP-1559 typed transactions. The bytes first must decode as a complete EIP-2718 typed envelope; if the envelope decodes but is any variant other than Eip1559 (legacy, EIP-2930 access-list, EIP-4844 blob, etc.), this bail fires. The library deliberately narrows to EIP-1559 because downstream identity/fee validation assumes 1559 fields.
Source
Thrown at crates/adapters/blockchain/src/execution/transaction.rs:204
pub chain_id: u64,
pub nonce: u64,
pub to: Address,
pub value: U256,
pub input: Bytes,
pub gas_limit: u64,
pub max_fee_per_gas: u128,
pub max_priority_fee_per_gas: u128,
}
/// Decodes and authenticates one complete signed EIP-1559 transaction.
pub(super) fn decode_signed_transaction(
raw_transaction: &[u8],
) -> anyhow::Result<DecodedSignedTransaction> {
let envelope = TxEnvelope::decode_2718_exact(raw_transaction).map_err(|_| {
anyhow::anyhow!("Persisted signed transaction is not a complete EIP-2718 envelope")
})?;
let TxEnvelope::Eip1559(signed) = envelope else {
anyhow::bail!("Persisted signed transaction is not EIP-1559");
};
anyhow::ensure!(
signed.signature().normalize_s().is_none(),
"Persisted transaction signature is not EIP-2 normalized"
);
let signer = signed
.signature()
.recover_address_from_prehash(&signed.signature_hash())
.context("failed to recover persisted transaction signer")?;
let hash = *signed.hash();
let tx = signed.tx();
let TxKind::Call(to) = tx.to else {
anyhow::bail!("Signed transaction creates a contract instead of calling a destination");
};
anyhow::ensure!(
tx.access_list.is_empty(),
"Signed transaction access list is not empty"
);View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the first byte of the raw transaction: values other than 0x02 indicate a non-EIP-1559 typed envelope; re-sign or re-persist the transaction as EIP-1559 (type 0x02).
- Check how the transaction was produced upstream (signer/wallet config) and force EIP-1559 signing (no access list, no blob fields).
- If legacy transactions are legitimate for your chain, decode them with a legacy path instead of routing them into decode_signed_transaction.
- Verify you persisted the signed EIP-1559 tx and not a differently-typed artifact (e.g. compare the tx hash/type from the signing step).
Example fix
// before let envelope = TxEnvelope::decode_2718_exact(raw)?; // may be Eip2930/Eip4844 from signer // after let envelope = TxEnvelope::decode_2718_exact(raw)?; assert!(matches!(envelope, TxEnvelope::Eip1559(_)), "signer must produce EIP-1559 (type 0x02) txs"); decode_signed_transaction(raw)?;
Defensive patterns
Strategy: validation
Validate before calling
fn is_eip1559_envelope(raw: &[u8]) -> bool {
TxEnvelope::decode_2718_exact(raw)
.map(|e| matches!(e, TxEnvelope::Eip1559(_)))
.unwrap_or(false)
}
// call decode_signed_transaction only if is_eip1559_envelope(raw) Type guard
fn as_eip1559(envelope: &TxEnvelope) -> Option<&Signed<TxLegacyOr1559Check>> {
match envelope { TxEnvelope::Eip1559(s) => Some(s), _ => None }
} Try / catch
match decode_signed_transaction(raw) {
Err(e) if e.to_string().contains("not EIP-1559") => /* re-sign as 1559 or route to legacy decoder */,
Err(e) => return Err(e),
Ok(decoded) => decoded,
} Prevention
- Pin the signer/wallet to EIP-1559 (type 0x02) transaction generation
- Check the first envelope byte (0x02 = EIP-1559) before persisting signed transactions
- Keep the persisted raw tx and its type metadata together so mismatches are caught at write time
- Test the signing path against the decoder in CI
When it happens
Trigger: Calling decode_signed_transaction (directly or via validate_rpc_transaction_matches_payload, verify_finalized_transaction_identity, validate_signed_transaction) with raw_transaction bytes that are an EIP-2718 envelope of a non-1559 type: a legacy signed tx, an EIP-2930 tx, or an EIP-4844 blob tx.
Common situations: Persisting transactions produced by an older signing path or wallet that emits legacy transactions; a chain/wallet upgrade switching the adapter to EIP-2930 or EIP-4844 typed txs; storing the wrong artifact (e.g. a 2930 tx built with an access list) and later replaying it through this decoder.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Failed to load active execution intent: {e}
- Failed to start replacement transaction persistence: {e}
- Failed to lock active execution intent {intent_id}: {e}
- Active execution intent {intent_id} was not found
- Invalid execution transition for intent {intent_id}: {curren
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/bd751f69af535cfa.
Report an issue: GitHub.