linera-io/linera-protocol · error · ExecutionError
ServiceOracleResponseTooLarge
ServiceOracleResponseTooLarge
Error message
ExecutionError::ServiceOracleResponseTooLarge
What it means
Every oracle response is size-checked by track_service_oracle_response: if the serialized response exceeds the policy's maximum_oracle_response_bytes, the query fails with ServiceOracleResponseTooLarge (resources.rs:815). Oracle responses are recorded and replayed, so their size must stay bounded.
Source
Thrown at linera-execution/src/resources.rs:815
let spent_execution_time = &mut tracker.service_oracle_execution;
let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
*spent_execution_time = spent_execution_time.saturating_add(execution_time);
ensure!(
*spent_execution_time < limit,
ExecutionError::MaximumServiceOracleExecutionTimeExceeded
);
Ok(())
}
/// Tracks the size of a response produced by an oracle.
pub(crate) fn track_service_oracle_response(
&self,
response_bytes: usize,
) -> Result<(), ExecutionError> {
ensure!(
response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
ExecutionError::ServiceOracleResponseTooLarge
);
Ok(())
}
}
impl<Account, Tracker> ResourceController<Account, Tracker>
where
Tracker: AsMut<ResourceTracker>,
{
/// Tracks the serialized size of a block, or parts of it.
pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
self.track_block_size(bcs::serialized_size(data)?)
}
/// Tracks the serialized size of a block, or parts of it.View on GitHub (pinned to 6c226ddcb3)
Solutions
- Paginate or project the response: return only the needed fields and rows per query
- Move large payloads out of band via blobs and return only a BlobId or hash from the query
- If you operate the network, raise maximum_oracle_response_bytes in the committee policy
Example fix
// before
fn handle_query(_: Query) -> Response { Response::All(self.items.clone()) }
// after
fn handle_query(q: Query) -> Response {
Response::Page(self.items.iter().skip(q.offset).take(q.limit).cloned().collect())
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the serialized response size before returning from the service handler
fn response_fits(resp: &Response, policy: &ResourceControlPolicy) -> Result<bool, Error> {
Ok(bcs::serialized_size(resp)? as u64 <= policy.maximum_oracle_response_bytes)
}
let resp = self.handle_query(q)?;
if !response_fits(&resp, policy)? {
return Err(app_error!("query response exceeds oracle size limit; paginate"));
} Type guard
fn is_oracle_response_too_large(err: &ExecutionError) -> bool {
matches!(err, ExecutionError::ServiceOracleResponseTooLarge)
} Try / catch
match contract.call_service_oracle(app_id, query).await {
Ok(bytes) => bytes,
Err(ref e) if is_oracle_response_too_large(e) => {
// deterministic: re-ask with pagination instead of retrying as-is
contract.call_service_oracle(app_id, paginated(query, 0, page_size)).await
}
Err(e) => return Err(e.into()),
} Prevention
- Paginate every query that can return unbounded collections
- Return blob ids or hashes for large payloads, not the payload itself
- Assert serialized response sizes in service unit tests against the policy limit
When it happens
Trigger: A service-oracle query returns a payload whose serialized size exceeds maximum_oracle_response_bytes, e.g. a query that returns a whole table, a large collection, or an unbounded result set.
Common situations: Queries returning all items instead of a page; missing pagination; page size configured above the policy; policies lowered between epochs; returning file-like payloads in a query response.
Related errors
- MaximumServiceOracleExecutionTimeExceeded
- Empty blocks are not allowed
- ServiceOracleQueryOperations
- MaximumFuelExceeded
- no signer found for owner ${owner}
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/94d39e7dcbfe07fb.
Report an issue: GitHub.