linera-io/linera-protocol · error · ExecutionError

OutdatedUpdateStream

OutdatedUpdateStream

Error message

ExecutionError::OutdatedUpdateStream

What it means

UpdateStream is an internal system operation that chain clients create to advance a subscriber's position in a publisher's event stream (linera-core/src/client/chain_client/mod.rs:706). Execution (system.rs:631) requires the stored per-application next index for that subscription to be strictly lower than the incoming next_index; an operation that carries no progress is rejected as OutdatedUpdateStream to keep subscription state monotonic.

Source

Thrown at linera-execution/src/system.rs:631

                self.committee_hash.set(Some(event_data.blob_hash));
                self.epoch.set(epoch);
            }
            UpdateStream {
                application_id,
                chain_id,
                stream_id,
                first_index,
                next_index,
            } => {
                let subscriptions = self
                    .event_subscriptions
                    .get_mut_or_default(&(chain_id, stream_id.clone()))
                    .await?;
                let app_next_index = *subscriptions
                    .applications
                    .get(&application_id)
                    .ok_or(ExecutionError::UnsubscribedUpdateStream)?;
                ensure!(
                    app_next_index < next_index,
                    ExecutionError::OutdatedUpdateStream
                );
                txn_tracker.add_stream_to_process(
                    application_id,
                    chain_id,
                    stream_id.clone(),
                    app_next_index,
                    first_index,
                    next_index,
                );
                subscriptions
                    .applications
                    .insert(application_id, next_index);
                subscriptions.recalculate_min();
                let index = next_index
                    .checked_sub(1)
                    .ok_or(ArithmeticError::Underflow)?;

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Rebuild the operations from the chain's current state immediately before signing: linera-core itself reads get_stream_indices and filters with app_index < next_index, so avoid caching these operations.
  2. Deduplicate UpdateStream operations in a batch and drop any already covered by the chain's stored indices.
  3. Run only one writer per chain for stream processing (or serialize submissions) so updates are not raced.
  4. In tests, derive first_index/next_index from the publisher's actual stream counts instead of constants.

Example fix

// before: op built from stale/cached state
let op = SystemOperation::UpdateStream { application_id, chain_id, stream_id, first_index: 0, next_index: 5 };

// after: recompute from the node right before submitting
let counts = local_node.get_stream_indices(publisher_chain_id, stream_id.clone()).await?;
let op = SystemOperation::UpdateStream {
    application_id, chain_id: publisher_chain_id, stream_id,
    first_index: counts.first_index, next_index: counts.next_index,
}; // and skip when counts.next_index <= stored subscription index
Defensive patterns

Strategy: validation

Validate before calling

// recompute from the node immediately before signing; skip stale updates
let counts = local_node.get_stream_indices(publisher_chain_id, stream_id.clone()).await?;
let stored = subscriptions.applications.get(&app_id).copied().unwrap_or(0);
let ops = if counts.next_index > stored {
    vec![SystemOperation::UpdateStream {
        application_id: app_id, chain_id: publisher_chain_id, stream_id,
        first_index: counts.first_index, next_index: counts.next_index,
    }.into()]
} else { vec![] }; // nothing to do: avoids OutdatedUpdateStream

Try / catch

match result {
    Err(ExecutionError::OutdatedUpdateStream) => {
        // rebuild the operation batch from fresh chain state and resubmit;
        // the stale operation itself must be discarded, not retried
    }
    other => other,
}

Prevention

When it happens

Trigger: Submitting an UpdateStream whose next_index is less than or equal to the subscription's stored index: operations built from stale chain state, the same update included twice in one batch, or two processes racing to process the same subscription so the second submission is already covered.

Common situations: Re-running or replaying stream-processing blocks; tests constructing UpdateStream operations by hand with hardcoded indices; running multiple clients/workers against the same chain concurrently; a client that fetched stream counts, then delayed long enough for another writer to advance the subscription.

Related errors


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