linera-io/linera-protocol · error · EvmExecutionError
It is illegal to call function execute_message from an opera
Error message
It is illegal to call function execute_message from an operation
What it means
Raised by linera-execution's forbid_execute_operation_origin, called from the EVM module's execute_operation and init_transact: the 4-byte selector at the start of the operation's calldata must not equal EXECUTE_MESSAGE_SELECTOR ([173, 125, 234, 205]), the reserved entry point that only the system may invoke when delivering a cross-chain message. Submitting an operation whose target function collides with that selector is rejected before execution because user operations must enter the contract through ordinary entry points.
Source
Thrown at linera-execution/src/evm/inputs.rs:169
/// only from a submitted message
pub(crate) const EXECUTE_MESSAGE_SELECTOR: &[u8] = &[173, 125, 234, 205];
/// This is the selector of `process_streams` that should be called
/// only from a submitted message
pub(crate) const PROCESS_STREAMS_SELECTOR: &[u8] =
&<process_streamsCall as alloy_sol_types::SolCall>::SELECTOR;
/// This is the selector of `summarize_events`, which is called by the system on a
/// checkpoint and never from a submitted operation.
pub(crate) const SUMMARIZE_EVENTS_SELECTOR: &[u8] =
&<summarize_eventsCall as alloy_sol_types::SolCall>::SELECTOR;
/// This is the selector of `instantiate` that should be called
/// only when creating a new instance of a shared contract
pub(crate) const INSTANTIATE_SELECTOR: &[u8] = &[156, 163, 60, 158];
pub(crate) fn forbid_execute_operation_origin(vec: &[u8]) -> Result<(), EvmExecutionError> {
ensure!(
vec != EXECUTE_MESSAGE_SELECTOR,
EvmExecutionError::IllegalOperationCall("function execute_message".to_string(),)
);
ensure!(
vec != PROCESS_STREAMS_SELECTOR,
EvmExecutionError::IllegalOperationCall("function process_streams".to_string(),)
);
ensure!(
vec != SUMMARIZE_EVENTS_SELECTOR,
EvmExecutionError::IllegalOperationCall("function summarize_events".to_string(),)
);
ensure!(
vec != INSTANTIATE_SELECTOR,
EvmExecutionError::IllegalOperationCall("function instantiate".to_string(),)
);
Ok(())
}
View on GitHub (pinned to 6c226ddcb3)
Solutions
- Rename the Solidity function or change its signature so its selector differs (any change to name/arg types rehashes the selector)
- If you meant to deliver a message, use Linera's message sending instead of an operation
- Check the operation payload's first four bytes before submission and reject 0xad7deacd client-side
Example fix
// Solidity: rename to change the selector
// before
function execute_message(bytes calldata data) external { ... }
// after
function handle_incoming(bytes calldata data) external { ... } Defensive patterns
Strategy: validation
Validate before calling
const EXECUTE_MESSAGE_SELECTOR: [u8; 4] = [173, 125, 234, 205];
fn targets_reserved_entrypoint(calldata: &[u8]) -> bool {
calldata.get(..4) == Some(&EXECUTE_MESSAGE_SELECTOR[..])
}
assert!(!targets_reserved_entrypoint(&operation_calldata), "operations must not call execute_message"); Type guard
fn is_illegal_execute_message_call(err: &ExecutionError) -> bool {
matches!(
err,
ExecutionError::EvmError(evm_error!::IllegalOperationCall(ref f)) if f.contains("execute_message")
)
} Try / catch
match client.execute_operations(ops, vec![]).await {
Err(e) if is_illegal_execute_message_call(&e) => {
// Reject the offending operation at the source; rename the contract entry point.
Err(anyhow::anyhow!("operation targets reserved execute_message selector: {e}"))
}
other => other,
} Prevention
- Never name a public contract function execute_message with the system signature; rename wrapper functions
- Validate calldata selectors client-side before submitting operations (the 4-byte check above)
- Deliver messages through Linera's message/stream API, not by invoking message entry points from operations
When it happens
Trigger: An EVM operation whose calldata starts with bytes 0xad7deacd — either deliberately calling execute_message from an operation, or a Solidity function whose ABI hash happens to collide with the reserved selector; transact requests (init_transact) routing the same selector.
Common situations: Porting contracts that expose a function named execute_message(bytes); rare 4-byte ABI collisions with unrelated function signatures; tooling that forwards message payloads as operations instead of sending messages.
Related errors
- It is illegal to call function process_streams from an opera
- It is illegal to call function summarize_events from an oper
- It is illegal to call function instantiate from an operation
- The balances are incoherent for address {0}, balances {1}, {
- The function {0} is being called but is missing from the byt
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/526c5de1ad61652a.
Report an issue: GitHub.