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

  1. Rename the Solidity function or change its signature so its selector differs (any change to name/arg types rehashes the selector)
  2. If you meant to deliver a message, use Linera's message sending instead of an operation
  3. 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

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


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/526c5de1ad61652a. Report an issue: GitHub.