19 Oct 2026, Mon

Solidity 0.8.37 Patches Intermediate Representation Bug Affecting Custom Error Arguments in Require Statements

The Ethereum Foundation’s Solidity development team has officially released Solidity version 0.8.37, introducing a critical fix for a subtle code generation bug that affected how custom error arguments are handled within require statements. Discovered earlier in the month on February 4, 2026, the vulnerability was brought to light through the official Ethereum Foundation bug bounty program by security researcher Carl from Cantina Security. While the flaw has now been fully addressed, it highlights the complex interactions between high-level language abstractions and low-level stack memory management within modern smart contract compilers.

The underlying defect specifically affected the Intermediate Representation (IR)-based code generator pipeline. It materialized when developers passed a custom error as the second argument to a require statement using named-parameter syntax in an order that diverged from the error’s original declaration. Rather than placing the arguments onto the execution stack in the declaration order expected by the ABI encoder, the compiler erroneously pushed them in call-site order. Consequently, when a transaction reverted, the resulting error data payload was misaligned, causing off-chain decoders to interpret the arguments incorrectly.

Origins and Technical Scope of the Compiler Defect

The bug was originally introduced in Solidity 0.8.26, a version that expanded language capabilities by adding native support for passing custom errors directly into require statements as a mechanism for providing rich revert context. Prior to this feature, developers typically relied on standalone revert CustomError(...) statements to handle error conditions.

Crucially, the compiler’s code generation path for standalone revert statements handled named arguments correctly by reordering them prior to ABI encoding. However, the newly introduced require-with-custom-error path in the IR pipeline inadvertently bypassed this essential reordering step. As a result, the compiler emitted code that evaluated arguments and pushed them onto the stack in the exact sequence they appeared in the source code at the call site. Meanwhile, the ABI encoder processed the stack strictly according to the parameter layout defined in the custom error’s original declaration.

This mismatch created a silent data corruption pattern during transaction reverts. The stack itself is merely a sequential array of untyped slots, with no built-in markers indicating where one argument terminates and another begins. When the encoder consumed these misordered slots, it mapped values to the wrong parameter identifiers.

The consequences of this misalignment varied depending on the data types involved. When developers swapped two arguments of identical types—such as two uint256 variables—the compiler detected no type mismatch, and the type checker accepted the permutation without warning. Off-chain consumers parsing the resulting revert data would subsequently read the swapped values, receiving inverted numbers without any indication that an error had occurred during compilation.

Manifestation Across Value and Reference Types

The severity of the misencoding scaled significantly when the transposed arguments possessed different data types or occupied varying numbers of stack slots on the Virtual Machine execution layer. When reordering parameters of different types that coincidentally shared the same number of stack slots—such as pairing a uint256 with an address—the structural validity of the encoded payload remained intact. Each value simply landed in the alternate parameter’s storage slot, resulting in cryptographically valid yet semantically incorrect data.

The behavior grew more problematic when reference types and multi-slot arguments entered the equation. Certain types, such as external function pointers or dynamic calldata arrays, require multiple stack slots to store lengths and memory offsets, whereas standard value types require only a single slot. String literals, by contrast, occupy zero stack slots because their values are hard-coded directly into the encoder.

When developers mixed multi-slot dynamic types with single-slot value types using out-of-order named parameters, the argument boundaries became completely skewed. For example, in a scenario involving a custom error declaration expecting a string followed by a uint256, passing the integer first and the string second caused the encoder to misinterpret the integer’s value as a memory offset. This frequently triggered a secondary, lower-level EVM execution error, forcing the transaction to revert with entirely opaque, undecodable bytes rather than the intended custom error payload.

In other instances, the scrambled data coincidentally formed a structurally valid ABI-encoded payload. Off-chain applications, block explorers, and testing suites attempting to decode the transaction logs would successfully parse the payload, extracting plausible-looking numbers or strings that bore zero relation to the actual values passed by the smart contract’s execution logic.

Real-World Implications and Ecosystem Standards

The potential fallout of this bug primarily centers on off-chain readability and ecosystem integrations. Because the defect exclusively manifested during the construction of revert data for transactions that were already destined to fail, it had zero impact on the on-chain state, storage variables, return values, or execution paths of successful transactions. A reverting transaction cannot modify state regardless of the error payload it carries.

Nevertheless, modern decentralized applications increasingly rely on structured revert data to communicate failure states to front-end interfaces, indexers, automated testing frameworks, and advanced protocol layers. A prominent example highlighted by the Solidity team involves ERC-3668, commonly known as CCIP Read. This standard relies heavily on clients decoding the OffchainLookup custom error to orchestrate off-chain data retrieval securely. Because OffchainLookup incorporates a mixture of dynamic and value types, an unaddressed argument reordering within an affected require statement could generate a cleanly decoding payload carrying fundamentally corrupted parameters. This would break complex data-fetching workflows in a manner that is exceptionally difficult for end-users or client applications to diagnose.

Despite these theoretical risks, the practical likelihood of encountering the bug in production was exceptionally low. The vulnerable code path required a highly specific combination of factors: usage of the IR-based pipeline, implementation of a custom error within a require statement, and the application of named-parameter syntax in a non-declaration order. Furthermore, named-argument syntax is relatively uncommon within require calls across the broader Solidity ecosystem, explaining why the defect remained latent in the codebase for nearly two years.

Severity Assessment and Resolution

Following the disclosure by Carl via the Ethereum Foundation bug bounty program, the Solidity security team conducted a thorough evaluation of the defect. They assigned the bug a "very low" severity rating. This classification was driven by the narrow trigger conditions, the read-only nature of the affected revert data path, and the absence of any viable vector for third-party exploitation. An external attacker could not maliciously induce this mis-encoding in a securely written contract, as the flawed named-argument structure had to be hardcoded directly into the contract’s original source code.

To ensure comprehensive safety across the language, the core developers performed an exhaustive audit of all other language constructs that share similar compilation and encoding pathways. Testing confirmed that these alternative pathways were entirely unaffected by the issue.

With the launch of Solidity version 0.8.37, the code generation logic for require statements utilizing custom errors has been fully corrected to ensure that named arguments are properly reordered to match their official declaration layout prior to ABI encoding. Developers utilizing the IR pipeline are encouraged to review their codebase for named-argument require patterns and update their compiler versions to 0.8.37 to maintain absolute integrity in their protocol’s off-chain error reporting.

Leave a Reply

Your email address will not be published. Required fields are marked *