{"record":{"id":"f0fb422d90a857fd","repo":"linera-io/linera-protocol","slug":"closedchain","errorCode":"ClosedChain","errorMessage":"Closed chains cannot have operations, accepted messages or empty blocks","messagePattern":"Closed chains cannot have operations, accepted messages or empty blocks","errorType":"error_code","errorClass":"ChainError","httpStatus":null,"severity":"error","filePath":"linera-chain/src/block_tracker.rs","lineNumber":272,"sourceCode":"        let _message_latency = message_latency.measure_latency_us();\n        let context = MessageContext {\n            chain_id: self.chain_id,\n            origin: incoming_bundle.origin,\n            origin_timestamp: incoming_bundle.bundle.timestamp,\n            is_bouncing: posted_message.is_bouncing(),\n            height: self.block_height,\n            round,\n            authenticated_owner: posted_message.authenticated_owner,\n            refund_grant_to: posted_message.refund_grant_to,\n            timestamp: self.timestamp,\n        };\n        let mut grant = posted_message.grant;\n        match incoming_bundle.action {\n            MessageAction::Accept => {\n                let chain_execution_context =\n                    ChainExecutionContext::IncomingBundle(txn_tracker.transaction_index());\n                // Once a chain is closed, accepting incoming messages is not allowed.\n                ensure!(!chain.system.closed.get(), ChainError::ClosedChain);\n\n                let mut actor =\n                    ExecutionStateActor::new(chain, txn_tracker, self.resource_controller);\n                Box::pin(actor.execute_message(\n                    context,\n                    posted_message.message.clone(),\n                    (grant > Amount::ZERO).then_some(&mut grant),\n                ))\n                .await\n                .with_execution_context(chain_execution_context)?;\n                actor\n                    .send_refund(context, grant)\n                    .with_execution_context(chain_execution_context)?;\n            }\n            MessageAction::Reject => {\n                // If rejecting a message fails, the entire block proposal should be\n                // scrapped.\n                ensure!(","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/block_tracker.rs#L254-L290","documentation":"During block execution, execute_message_in_block handles incoming message bundles. For MessageAction::Accept it enforces at linera-chain/src/block_tracker.rs:272 that the target chain is not closed (ChainError::ClosedChain). A closed chain is administratively shut down and must not accept operations, messages, or produce blocks — so a block proposal that accepts an incoming bundle on a closed chain is rejected outright, failing the whole block.","triggerScenarios":"A client proposes a block containing an incoming bundle with MessageAction::Accept after the chain's system.closed flag was set by an admin CloseChain operation; a validator processes a delayed/retried proposal that raced with the close transaction; a stale wallet/client keeps submitting blocks against a chain that was closed between queries.","commonSituations":"Chain owner closed the chain (e.g., migrating to a new chain) but a service or bot still proposes blocks with pending incoming messages; testing against a chain previously closed in an earlier test case; race between querying chain state and proposing the block.","solutions":["Check the chain's closed status (system.closed) before constructing or submitting a block that accepts messages.","Stop and decommission clients/bots targeting the closed chain; point them at the successor chain.","If the close was unintentional, the chain owner must address chain administration — a closed chain cannot be reopened by proposing blocks.","Handle ChainError::ClosedChain in the proposer loop by abandoning retries for that chain instead of resubmitting the same block."],"exampleFix":"// before\nlet actions = vec![IncomingBundle { action: MessageAction::Accept, ..bundle }];\nblock_proposal.submit().await?;\n\n// after\nensure!(!client.chain_is_closed(chain_id).await?, \"chain {chain_id} is closed; do not propose message-accepting blocks\");\nlet actions = vec![IncomingBundle { action: MessageAction::Accept, ..bundle }];\nblock_proposal.submit().await?;","handlingStrategy":"validation","validationCode":"// Before proposing a block that accepts incoming bundles:\nlet chain_info = client.describe_chain(chain_id).await?;\nif chain_info.system_context.closed {\n    anyhow::bail!(\"chain {chain_id} is closed; cannot accept incoming messages\");\n}\n// ... build and submit the block with MessageAction::Accept bundles","typeGuard":null,"tryCatchPattern":"match result {\n    Ok(outcome) => outcome,\n    Err(ChainError::ClosedChain) => {\n        // Stop retrying: the chain is administratively closed.\n        tracing::info!(chain_id = %chain_id, \"chain closed; dropping pending proposals\");\n        self.retire_chain(chain_id);\n        return Ok(());\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Query closed status immediately before block submission to shrink the race window.","Monitor for admin close-chain operations on chains your clients serve and drain gracefully.","Configure proposers to abandon (not retry) on ClosedChain.","In multi-chain services, map chain lifecycle state and gate message-accepting proposals on it."],"tags":["linera","blockchain","chain-state","message-execution","rust"],"backgroundTag":"closed-chain-operation-rejected","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}