linera-io/linera-protocol · error · NodeError
Unexpected certificate value
Error message
Unexpected certificate value
What it means
Raised in RemoteNode::download_certificates_by_heights while validating the batch a validator returned for a download_certificates_by_heights(chain_id, heights) call: every certificate whose inner chain_id differs from the requested chain_id triggers this error. It is a response-integrity check — a well-behaved validator must only return certificates of the chain that was asked for.
Source
Thrown at linera-core/src/remote_node.rs:261
&self,
chain_id: ChainId,
heights: Vec<BlockHeight>,
) -> Result<Vec<ConfirmedBlockCertificate>, NodeError> {
let mut expected_heights = VecDeque::from(heights.clone());
let certificates = self
.node
.download_certificates_by_heights(chain_id, heights)
.await?;
if certificates.len() > expected_heights.len() {
return Err(NodeError::TooManyCertificatesReturned {
chain_id,
remote_node: Box::new(self.public_key),
});
}
for certificate in &certificates {
ensure!(
certificate.inner().chain_id() == chain_id,
NodeError::UnexpectedCertificateValue
);
if let Some(expected_height) = expected_heights.pop_front() {
ensure!(
expected_height == certificate.inner().height(),
NodeError::UnexpectedCertificateValue
);
} else {
return Err(NodeError::UnexpectedCertificateValue);
}
}
ensure!(
expected_heights.is_empty(),
NodeError::MissingCertificatesByHeights {
chain_id,
heights: expected_heights.into_iter().collect(),View on GitHub (pinned to 6c226ddcb3)
Solutions
- Exclude the offending validator from the download and retry with the remaining ones
- Check the validator's and any proxy's configuration for cross-chain response mixing
- Verify the requested chain_id and heights are what you intended (copy/paste mistakes make legitimate responses look wrong)
- Report the validator if it reproducibly returns foreign certificates
Defensive patterns
Strategy: fallback
Type guard
fn is_unexpected_certificate_value(err: &NodeError) -> bool {
matches!(err, NodeError::UnexpectedCertificateValue)
} Try / catch
for node in validator_nodes {
match remote_node.download_certificates_by_heights(chain_id, heights.clone()).await {
Ok(certs) => return Ok(certs),
Err(NodeError::UnexpectedCertificateValue) => continue, // skip bad validator
Err(e) => return Err(e.into()),
}
}
return Err(NodeError::UnexpectedCertificateValue.into()); Prevention
- Double-check the chain_id passed to download calls — a wrong id makes honest answers look invalid
- Fence validators that repeatedly return foreign certificates from your download sources
- Prefer height-ordered batch downloads from validators you health-check regularly
When it happens
Trigger: Validator (or proxy) returns certificates belonging to another chain; corrupted deserialization routing responses between tenants; byzantine validator serving unrelated data. Only bites per-validator responses inspected directly; the client's scheduler hedges across validators and can drop a bad source.
Common situations: Shared validator infrastructure mixing responses across chains after a misconfiguration; a stale proxy cache serving another chain's certificates; adversarial testing of validator honesty.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Failed to download certificates and update local node to the
- The received chain info response is invalid
- Missing certificates for chain {chain_id} in heights {height
- Node returned a BlobsNotFound error with an empty list of mi
- Local error handling validator response: validator still rep
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/f44b572ec07a118a.
Report an issue: GitHub.