{"record":{"id":"180dadce34bd9bfe","repo":"linera-io/linera-protocol","slug":"nottimedoutyet","errorCode":"NotTimedOutYet","errorMessage":"Not signing timeout certificate; current round times out at time {0}","messagePattern":"Not signing timeout certificate; current round times out at time (.+?)","errorType":"validation","errorClass":"ChainError","httpStatus":null,"severity":"warning","filePath":"linera-chain/src/manager.rs","lineNumber":394,"sourceCode":"        &mut self,\n        chain_id: ChainId,\n        height: BlockHeight,\n        round: Round,\n        epoch: Epoch,\n        key_pair: Option<&ValidatorSecretKey>,\n        local_time: Timestamp,\n    ) -> Result<bool, ChainError> {\n        let Some(key_pair) = key_pair else {\n            return Ok(false); // We are not a validator.\n        };\n        ensure!(\n            round == self.current_round(),\n            ChainError::WrongRound(self.current_round())\n        );\n        let Some(round_timeout) = *self.round_timeout.get() else {\n            return Err(ChainError::RoundDoesNotTimeOut);\n        };\n        ensure!(\n            local_time >= round_timeout,\n            ChainError::NotTimedOutYet(round_timeout)\n        );\n        if let Some(vote) = self.timeout_vote.get() {\n            if vote.round == round {\n                return Ok(false); // We already signed this timeout.\n            }\n        }\n        let value = Timeout::new(chain_id, height, epoch);\n        self.timeout_vote\n            .set(Some(Vote::new(value, round, key_pair)));\n        Ok(true)\n    }\n\n    /// Signs a `Timeout` certificate to switch to fallback mode.\n    ///\n    /// This must only be called after verifying that the condition for fallback mode is\n    /// satisfied locally.","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/manager.rs#L376-L412","documentation":"create_timeout_vote refuses to sign a timeout before the round's deadline: the validator's local time must have reached round_timeout, the timestamp stored when the round opened. The error returns that deadline so callers know when to come back.","triggerScenarios":"vote_for_leader_timeout / create_timeout_vote invoked with local_time < round_timeout — requesting a timeout vote too early in the current round.","commonSituations":"Client clock ahead of the validator's clock; retry loop that does not respect the deadline; round_timeout duration configured longer on the validator than the client assumes; tests with mocked time.","solutions":["Wait until the round_timeout timestamp returned in the error, then retry the leader-timeout query","Verify clock synchronization (NTP) between client and validator","Derive wait durations from the validator's own timestamps in ChainInfoResponse rather than local assumptions"],"exampleFix":"// before: immediate retry, fails with NotTimedOutYet(round_timeout)\nclient.request_leader_timeout(chain_id, height, round).await?;\n\n// after: honor the deadline returned by the error (or chain info), then retry\nmatch client.request_leader_timeout(chain_id, height, round).await {\n    Err(e) if matches!(e, ref x if x.is_not_timed_out_yet()) => {\n        let deadline = e.not_timed_out_yet_deadline().unwrap();\n        tokio::time::sleep_until(deadline.into()).await;\n        client.request_leader_timeout(chain_id, height, round).await?;\n    }\n    other => other?,\n}","handlingStrategy":"retry","validationCode":"// Read the round deadline from chain info before asking for a timeout vote.\nlet info = client.chain_info(chain_id).await?;\nif let Some(deadline) = info.manager.round_timeout {\n    if clock.now() < deadline {\n        tokio::time::sleep_until(deadline.into()).await;\n    }\n}\nclient.request_leader_timeout(chain_id, height, round).await?;","typeGuard":"fn is_not_timed_out_yet(e: &ChainError) -> bool {\n    matches!(e, ChainError::NotTimedOutYet(_))\n}","tryCatchPattern":"match client.request_leader_timeout(chain_id, height, round).await {\n    Err(ChainError::NotTimedOutYet(deadline)) => {\n        tokio::time::sleep_until(deadline.into()).await;\n        client.request_leader_timeout(chain_id, height, round).await?;\n    }\n    other => other?,\n}","preventionTips":["Use the validator's timestamps (ChainInfoResponse), not local clock assumptions, to schedule timeout retries","Keep client and validator clocks NTP-synchronized","Bound retries with backoff so a misconfigured round duration surfaces quickly"],"tags":["consensus","timeout","leader-timeout","clock-sync","linera"],"backgroundTag":"timeout-not-elapsed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}