{"record":{"id":"f8ed9a2b6348aab5","repo":"linera-io/linera-protocol","slug":"outdatedupdatestream","errorCode":"OutdatedUpdateStream","errorMessage":"ExecutionError::OutdatedUpdateStream","messagePattern":"ExecutionError::OutdatedUpdateStream","errorType":"exception","errorClass":"ExecutionError","httpStatus":null,"severity":"error","filePath":"linera-execution/src/system.rs","lineNumber":631,"sourceCode":"                self.committee_hash.set(Some(event_data.blob_hash));\n                self.epoch.set(epoch);\n            }\n            UpdateStream {\n                application_id,\n                chain_id,\n                stream_id,\n                first_index,\n                next_index,\n            } => {\n                let subscriptions = self\n                    .event_subscriptions\n                    .get_mut_or_default(&(chain_id, stream_id.clone()))\n                    .await?;\n                let app_next_index = *subscriptions\n                    .applications\n                    .get(&application_id)\n                    .ok_or(ExecutionError::UnsubscribedUpdateStream)?;\n                ensure!(\n                    app_next_index < next_index,\n                    ExecutionError::OutdatedUpdateStream\n                );\n                txn_tracker.add_stream_to_process(\n                    application_id,\n                    chain_id,\n                    stream_id.clone(),\n                    app_next_index,\n                    first_index,\n                    next_index,\n                );\n                subscriptions\n                    .applications\n                    .insert(application_id, next_index);\n                subscriptions.recalculate_min();\n                let index = next_index\n                    .checked_sub(1)\n                    .ok_or(ArithmeticError::Underflow)?;","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-execution/src/system.rs#L613-L649","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Deduplicate UpdateStream operations in a batch and drop any already covered by the chain's stored indices.","Run only one writer per chain for stream processing (or serialize submissions) so updates are not raced.","In tests, derive first_index/next_index from the publisher's actual stream counts instead of constants."],"exampleFix":"// before: op built from stale/cached state\nlet op = SystemOperation::UpdateStream { application_id, chain_id, stream_id, first_index: 0, next_index: 5 };\n\n// after: recompute from the node right before submitting\nlet counts = local_node.get_stream_indices(publisher_chain_id, stream_id.clone()).await?;\nlet op = SystemOperation::UpdateStream {\n    application_id, chain_id: publisher_chain_id, stream_id,\n    first_index: counts.first_index, next_index: counts.next_index,\n}; // and skip when counts.next_index <= stored subscription index","handlingStrategy":"validation","validationCode":"// recompute from the node immediately before signing; skip stale updates\nlet counts = local_node.get_stream_indices(publisher_chain_id, stream_id.clone()).await?;\nlet stored = subscriptions.applications.get(&app_id).copied().unwrap_or(0);\nlet ops = if counts.next_index > stored {\n    vec![SystemOperation::UpdateStream {\n        application_id: app_id, chain_id: publisher_chain_id, stream_id,\n        first_index: counts.first_index, next_index: counts.next_index,\n    }.into()]\n} else { vec![] }; // nothing to do: avoids OutdatedUpdateStream","typeGuard":null,"tryCatchPattern":"match result {\n    Err(ExecutionError::OutdatedUpdateStream) => {\n        // rebuild the operation batch from fresh chain state and resubmit;\n        // the stale operation itself must be discarded, not retried\n    }\n    other => other,\n}","preventionTips":["Never cache UpdateStream operations across block submissions.","Run a single writer per chain for stream processing to avoid duplicate advances.","In tests, compute indices from actual stream state rather than hardcoded numbers."],"tags":["linera","event-streams","subscriptions","synchronization","internal"],"backgroundTag":"optimistic-concurrency-conflict","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}