linera-io/linera-protocol · error · EvmExecutionError
Contracts cannot call themselves
Error message
Contracts cannot call themselves
What it means
The Linera contract runtime precompile exposed to EVM contracts rejects tryCallApplication when the target application id equals the caller's own id (revm.rs:616). This is a direct reentrancy guard: re-entering your own application through the runtime while its execution is in flight would bypass fuel and state-consistency accounting.
Source
Thrown at linera-execution/src/evm/revm.rs:616
} => {
let authenticated = true;
let is_tracked = true;
let grant = Resources::default();
let send_message_request = SendMessageRequest {
destination,
authenticated,
is_tracked,
grant,
message,
};
let mut runtime = context.db().0.lock_runtime();
runtime.send_message(send_message_request)?;
Ok(vec![])
}
ContractRuntimePrecompile::TryCallApplication { target, argument } => {
let authenticated = true;
let mut runtime = context.db().0.lock_runtime();
ensure!(
target != runtime.application_id()?,
EvmExecutionError::NoSelfCall
);
runtime.try_call_application(authenticated, target, argument)
}
ContractRuntimePrecompile::Emit { stream_name, value } => {
let mut runtime = context.db().0.lock_runtime();
let result = runtime.emit(stream_name, value)?;
Ok(bcs::to_bytes(&result)?)
}
ContractRuntimePrecompile::ReadEvent {
chain_id,
stream_name,
index,
} => {
let mut runtime = context.db().0.lock_runtime();
runtime.read_event(chain_id, stream_name, index)
}View on GitHub (pinned to 6c226ddcb3)
Solutions
- Refactor: invoke the internal Solidity function directly instead of a tryCallApplication round-trip
- Guard the precompile call and revert early when target == own application id
- If recursion is genuinely needed, split the logic into two applications so the call is never self-directed
Example fix
// before: re-enter self through the runtime runtime.tryCallApplication(appId, data); // appId == this application // after: direct internal call, no runtime round-trip handleInternally(data);
Defensive patterns
Strategy: validation
Validate before calling
// Solidity: refuse to call ourselves through the runtime precompile
function callApp(address target, bytes memory data) internal {
require(target != OWN_APP_TARGET, NoSelfTarget());
LineraRuntime(PRECOMPILE).tryCallApplication(target, data);
} Type guard
fn is_no_self_call(err: &ExecutionError) -> bool {
matches!(err, ExecutionError::EvmError(EvmExecutionError::NoSelfCall))
} Try / catch
match runtime.try_call_application(target, arg) {
Ok(out) => out,
Err(ref e) if is_no_self_call(e) => {
// self-call attempted: run the logic internally instead
Ok(self.handle_internally(arg))
}
Err(e) => Err(e),
} Prevention
- Treat the runtime precompile as an inter-application API only; never point it at your own application id
- Keep reusable logic in internal Solidity functions and have entry points call those directly
- Add a test that forwards your own application id through every generic call path and asserts a clean application-level revert
When it happens
Trigger: Inside an EVM contract, calling the runtime precompile (address 0x0b) with ContractRuntimePrecompile::TryCallApplication where target equals the application id the contract itself is running as.
Common situations: Trying to reuse your own public entry point via the runtime instead of an internal Solidity function call; generic call-forwarding or fallback code that passes through whatever target it received (which may resolve to self); porting recursive application designs from other chains.
Related errors
- The balances are incoherent for address {0}, balances {1}, {
- Incorrect ApplicationId
- It is illegal to call function execute_message from an opera
- It is illegal to call function process_streams from an opera
- It is illegal to call function summarize_events from an oper
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/b66256302e8e4185.
Report an issue: GitHub.