{"record":{"id":"2a9f624dde69019d","repo":"linera-io/linera-protocol","slug":"the-signature-was-not-created-by-a-valid-entity","errorCode":null,"errorMessage":"The signature was not created by a valid entity","messagePattern":"The signature was not created by a valid entity","errorType":"validation","errorClass":"ChainError","httpStatus":null,"severity":"error","filePath":"linera-chain/src/data_types/mod.rs","lineNumber":1118,"sourceCode":"    {\n        let hash_and_round = VoteValue(\n            self.partial.hash(),\n            self.partial.round,\n            T::KIND,\n            self.partial.unlocking_round(),\n            self.partial.first_round(),\n            self.partial.justification_commitment(),\n        );\n        signature.check(&hash_and_round, public_key)?;\n        // Check that each validator only appears once.\n        ensure!(\n            !self.used_validators.contains(&public_key),\n            ChainError::CertificateValidatorReuse\n        );\n        self.used_validators.insert(public_key);\n        // Update weight.\n        let voting_rights = self.committee.weight(&public_key);\n        ensure!(voting_rights > 0, ChainError::InvalidSigner);\n        self.weight += voting_rights;\n        // Update certificate.\n        self.partial.add_signature((public_key, signature));\n\n        if self.weight >= self.committee.quorum_threshold() {\n            self.weight = 0; // Prevent from creating the certificate twice.\n            Ok(Some(self.partial.clone()))\n        } else {\n            Ok(None)\n        }\n    }\n}\n\n// Checks if the array slice is strictly ordered. That means that if the array\n// has duplicates, this will return False, even if the array is sorted\npub(crate) fn is_strictly_ordered(values: &[(ValidatorPublicKey, ValidatorSignature)]) -> bool {\n    values.windows(2).all(|pair| pair[0].0 < pair[1].0)\n}","sourceCodeStart":1100,"sourceCodeEnd":1136,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/data_types/mod.rs#L1100-L1136","documentation":"Thrown by SignatureAggregator::append when the signature's public key has zero voting weight in the aggregator's committee, i.e. the signer is not a member of the current committee (ChainError::InvalidSigner). The aggregator only counts signatures from validators with voting rights in the committee it was constructed with. A signature from an unknown or retired validator can never contribute to the quorum, so append rejects it immediately.","triggerScenarios":"Calling SignatureAggregator::append(public_key, signature) where committee.weight(&public_key) == 0. Happens when votes are collected from validators of a different epoch: e.g. a worker still holds votes signed under an old committee while the aggregator was built with the new one, or the operator feed maps a stale validator key set to a new committee.","commonSituations":"Epoch/committee rotations where in-flight votes from the outgoing committee reach the aggregator of the incoming one; misconfigured validator sets between nodes; test code generating signatures with keys never registered in the committee.","solutions":["Verify committee.weight(&public_key) > 0 before calling append, and skip/drop the vote when it is 0.","Check that the aggregator and the votes come from the same epoch — rebuild the aggregator with the committee of the epoch the votes were cast in.","If you aggregate votes received over the network, filter them against the current committee at receipt time so stale validators never reach the aggregator.","In tests, make sure the signing keys are taken from the same Committee builder used to construct the aggregator."],"exampleFix":"// before\nfor (key, sig) in votes {\n    if let Some(cert) = aggregator.append(key, sig)? {\n        return Ok(cert);\n    }\n}\n\n// after\nfor (key, sig) in votes {\n    if committee.weight(&key) == 0 {\n        continue; // not a member of this committee; skip\n    }\n    if let Some(cert) = aggregator.append(key, sig)? {\n        return Ok(cert);\n    }\n}","handlingStrategy":"validation","validationCode":"// before aggregating a vote\nif committee.weight(&public_key) == 0 {\n    tracing::warn!(?public_key, \"dropping vote from non-committee validator\");\n    continue;\n}","typeGuard":null,"tryCatchPattern":"match aggregator.append(key, sig) {\n    Ok(Some(cert)) => return Ok(cert),\n    Ok(None) => {}\n    Err(ChainError::InvalidSigner) => continue, // skip non-committee voter\n    Err(e) => return Err(e),\n}","preventionTips":["Always construct the SignatureAggregator with the committee of the epoch the votes belong to.","Filter incoming votes by committee membership at receipt time.","Log the offending public key to spot committee-configuration drift quickly."],"tags":["consensus","validators","committee","signatures"],"backgroundTag":"validator-not-in-committee","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}