linera-io/linera-protocol · error · ExecutionError
MaximumFuelExceeded
MaximumFuelExceeded
Error message
ExecutionError::MaximumFuelExceeded(vm_runtime)
What it means
track_fuel accumulates Wasm fuel consumed within a block via consume_fuel; when the running total exceeds the policy's maximum_wasm_fuel_per_block, execution fails with MaximumFuelExceeded(VmRuntime::Wasm) (resources.rs:496). Every Wasm instruction executed by the block's operations and messages is metered against this per-block cap.
Source
Thrown at linera-execution/src/resources.rs:496
.ok_or(ArithmeticError::Overflow)?;
self.update_balance(self.policy.http_request)
}
/// Tracks a number of fuel units used.
pub(crate) fn track_fuel(
&mut self,
fuel: u64,
vm_runtime: VmRuntime,
) -> Result<(), ExecutionError> {
match vm_runtime {
VmRuntime::Wasm => {
self.tracker.as_mut().wasm_fuel = self
.tracker
.as_ref()
.wasm_fuel
.checked_add(fuel)
.ok_or(ArithmeticError::Overflow)?;
ensure!(
self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
ExecutionError::MaximumFuelExceeded(vm_runtime)
);
}
VmRuntime::Evm => {
self.tracker.as_mut().evm_fuel = self
.tracker
.as_ref()
.evm_fuel
.checked_add(fuel)
.ok_or(ArithmeticError::Overflow)?;
ensure!(
self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
ExecutionError::MaximumFuelExceeded(vm_runtime)
);
}
}
self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)View on GitHub (pinned to 6c226ddcb3)
Solutions
- Split the work across multiple blocks (fewer operations per block)
- Optimize the contract: remove hot loops, cache results, reduce per-operation fuel
- If you operate the committee, raise maximum_wasm_fuel_per_block in the resource policy
Example fix
// before: all operations in one block
block.extend(operations);
propose(block)?;
// after: split into blocks sized under the per-block fuel cap
for chunk in operations.chunks(max_ops_per_block) {
propose(Block::new(chunk.to_vec()))?;
} Defensive patterns
Strategy: fallback
Validate before calling
// Estimate Wasm fuel per operation (from a benchmark or previous run) and
// cap the batch before proposing the block.
fn fits_fuel_budget(ops: &[Operation], est_fuel: impl Fn(&Operation) -> u64, max: u64) -> bool {
ops.iter().map(&est_fuel).try_fold(0u64, |acc, f| acc.checked_add(f))
.map(|total| total <= max)
.unwrap_or(false)
}
if !fits_fuel_budget(&ops, estimate, policy.maximum_wasm_fuel_per_block) {
ops.truncate(smaller_batch_len); // propose a smaller block
} Type guard
fn is_wasm_fuel_exceeded(err: &ExecutionError) -> bool {
matches!(err, ExecutionError::MaximumFuelExceeded(VmRuntime::Wasm))
} Try / catch
match proposer.propose(block).await {
Ok(h) => h,
Err(ref e) if is_wasm_fuel_exceeded(e) => {
// fallback: split the block in half and propose sequentially
let (a, b) = block.operations.split_at(block.operations.len() / 2);
propose_all(vec![a.to_vec(), b.to_vec()]).await
}
Err(e) => return Err(e.into()),
} Prevention
- Benchmark per-operation fuel and size batches against maximum_wasm_fuel_per_block
- Optimize hot loops in contracts before raising block sizes
- Monitor per-block fuel usage so batches stay under the cap with headroom
When it happens
Trigger: A block whose combined operations and messages burn more Wasm fuel than maximum_wasm_fuel_per_block: long-running loops in a contract, heavy computation, or too many operations batched into one block.
Common situations: Batching many operations into a single proposed block; compute-heavy contract upgrades; policy caps tightened by a new epoch; benchmark contracts with unbounded loops.
Related errors
- Empty blocks are not allowed
- Chain is expecting a next block at height {expected_block_he
- The previous block hash of a new block should match the last
- Block timestamp {new} must not be earlier than the parent bl
- Missing operations or messages from mandatory applications:
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/d3ba4eb9ae3423ea.
Report an issue: GitHub.