canopy-network/canopy · error · ErrInvalidRLPTx
ErrInvalidRLPTx
ErrInvalidRLPTx
Error message
max transaction size
What it means
rlpToCanopyTransaction enforces an anti-spam cap: the raw RLP-encoded Ethereum transaction must be at most 2 KB. Larger payloads are rejected as ErrInvalidRLPTx('max transaction size') before decoding.
Source
Thrown at fsm/ethereum.go:79
return nil, ErrInvalidRLPTx(err)
}
return tx.Hash().Bytes(), nil
}
// RLPToCanopyTransaction() converts a legacy-domain RLP transaction into a Canopy transaction.
func RLPToCanopyTransaction(txBytes []byte) (transaction *lib.Transaction, e lib.ErrorI) {
return rlpToCanopyTransaction(txBytes, RLPIndicator)
}
// RLPToCanopyTransactionV2() converts an RLP encoded transaction into a nonce-backed Canopy transaction.
func RLPToCanopyTransactionV2(txBytes []byte) (transaction *lib.Transaction, e lib.ErrorI) {
return rlpToCanopyTransaction(txBytes, RLPV2Indicator)
}
func rlpToCanopyTransaction(txBytes []byte, memo string) (transaction *lib.Transaction, e lib.ErrorI) {
// protect against spam
if len(txBytes) > int(2*units.KB) {
return nil, ErrInvalidRLPTx(fmt.Errorf("max transaction size"))
}
// decode transaction to ethereum object
var tx ethTypes.Transaction
if err := tx.UnmarshalBinary(txBytes); err != nil {
return nil, ErrInvalidRLPTx(err)
}
// get the signer type (supports: Legacy, EIP-155, EIP-1559, EIP-2930, EIP-4844, EIP-7702)
signer := ethTypes.LatestSignerForChainID(tx.ChainId())
// recover the public key from the rlp transaction and validate the signature
publicKey, err := crypto.RecoverPublicKey(signer, tx)
if err != nil {
return nil, ErrInvalidPublicKey(err)
}
// ensure the EVM chain id fits into Canopy's uint64 translation.
if tx.ChainId() == nil || !tx.ChainId().IsUint64() {
return nil, ErrInvalidRLPTx(fmt.Errorf("chain id exceeds uint64"))
}
// The signed Ethereum chain ID separates legacy RLP from RLP.V2 even whileView on GitHub (pinned to ee8197d91d)
Solutions
- Reduce the transaction payload size (trim data/calldata) below 2048 bytes before submission
- Split large operations into multiple transactions
- Check whether extra data is being accidentally appended to the RLP encoding
- If legitimate use needs more space, this is a protocol limit — file/track a protocol change rather than bypassing it
Example fix
// before
if len(txBytes) > 4096 { submit(txBytes) } // exceeds 2KB cap
// after
if len(txBytes) > int(2*units.KB) {
return fmt.Errorf("tx is %d bytes; RLP tx limit is 2048 bytes", len(txBytes))
}
submit(txBytes) Defensive patterns
Strategy: validation
Validate before calling
const maxRLPTx = 2 * 1024
if len(txBytes) > maxRLPTx {
return fmt.Errorf("tx %d bytes exceeds %d byte RLP limit", len(txBytes), maxRLPTx)
} Type guard
func fitsRLPLimit(b []byte) bool { return len(b) <= 2*1024 } Try / catch
tx, err := fsm.RLPToCanopyTransaction(bz)
if err != nil {
if strings.Contains(err.Error(), "max transaction size") {
return nil, fmt.Errorf("reduce payload to under 2048 bytes")
}
return err
} Prevention
- Enforce the 2048-byte cap in the submitting wallet/tool
- Keep memos/calldata minimal when bridging
- Test large payloads against the cap before broadcast
When it happens
Trigger: Submitting an RLP transaction (RLPToCanopyTransaction or RLPToCanopyTransactionV2) whose serialized bytes exceed 2*units.KB (2048 bytes), e.g. transactions carrying large calldata via tx.Data().
Common situations: Bridging Ethereum transactions with big memo/extraData payloads, contract-interaction blobs that exceed the cap, or malicious spam transactions crafted to bloat block space.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- nested transactions are not supported
- rollback is not supported for nested transactions
- root is not supported for nested transactions
AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06).
Data as JSON: /api/errors/5c0a9c863339f676.
Report an issue: GitHub.