linera-io/linera-protocol · error · EvmExecutionError
It is illegal to call function execute_message from an…
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.
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…
- It is illegal to call function summarize_events from an…
- It is illegal to call function instantiate from an operation
- Non-zero transfer precompile
- The balances are incoherent for address
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/526c5de1ad61652a.
Report an issue: GitHub.
Appendix: 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)