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

  1. Paginate or project the response: return only the needed fields and rows per query
  2. Move large payloads out of band via blobs and return only a BlobId or hash from the query
  3. 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

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


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