FuelLabs/fuel-core · error · Error::Serialization
Could not convert proof_set entry to Bytes32: {}
Error message
Could not convert proof_set entry to Bytes32: {} What it means
The upload proof_set is a list of 32-byte Merkle proofs. Each entry goes through Bytes32::try_from(entry.as_slice()); any entry whose length is not exactly 32 bytes fails and is wrapped with this message, failing the whole Upload conversion.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:794
u16::try_from(proto_upload.subsection_index).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert subsection_index to u16: {}",
e
))
})?;
let subsections_number = u16::try_from(proto_upload.subsections_number)
.map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert subsections_number to u16: {}",
e
))
})?;
let proof_set = proto_upload
.proof_set
.iter()
.map(|entry| {
Bytes32::try_from(entry.as_slice()).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert proof_set entry to Bytes32: {}",
e
))
})
})
.collect::<crate::result::Result<Vec<_>>>()?;
let body = UploadBody {
root,
witness_index,
subsection_index,
subsections_number,
proof_set,
};
let upload_tx =
FuelTransaction::upload(body, policies, inputs, outputs, witnesses);
View on GitHub (pinned to b9d4d170da)
Solutions
- Pre-validate: all proof_set entries must be exactly 32 bytes; report which index failed
- Fix the producer's Merkle proof generation to emit fuel-core-sized (32-byte) proofs
- Recompute the proof set from the blob root and subsection data instead of trusting the wire value if the source is recoverable
- Add payload integrity checks (re-serialization round-trip) at ingestion
Example fix
// before
let proof_set = proto_upload.proof_set.iter().map(|e| Bytes32::try_from(e.as_slice()).map_err(...)).collect::<Result<Vec<_>>>()?;
// after
if let Some(i) = proto_upload.proof_set.iter().position(|e| e.len() != 32) {
return Err(anyhow!("proof_set[{i}] has {} bytes, expected 32", proto_upload.proof_set[i].len()));
}
let proof_set = proto_upload.proof_set.iter().map(|e| Bytes32::try_from(e.as_slice()).unwrap()).collect::<Vec<_>>(); Defensive patterns
Strategy: validation
Validate before calling
fn proof_set_is_valid(u: &ProtoUpload) -> bool {
!u.proof_set.is_empty() && u.proof_set.iter().all(|e| e.len() == 32)
} Type guard
fn proof_entry_ok(e: &[u8]) -> bool { e.len() == 32 } Try / catch
for (i, entry) in u.proof_set.iter().enumerate() {
Bytes32::try_from(entry.as_slice())
.with_context(|| format!("proof_set[{i}] len {}", entry.len()))?;
} Prevention
- Validate every vector element's width, not just the first
- Generate proofs with the same 32-byte hash function the chain uses
- Include the failing index in error context so producers can locate the bug
When it happens
Trigger: An Upload proto where one or more proof_set entries are empty (producer pushed Vec::new()) or wrong-length (31/33 bytes) due to an encoder bug or payload corruption. Note the check runs per entry, so one bad proof invalidates the transaction even if the root is valid.
Common situations: Producer building proofs with a different hash width; empty proof vectors used as placeholders; truncation in transport/storage; test fixtures with dummy proofs.
Related errors
- Could not convert upload root to Bytes32: {}
- Could not convert witness_index to u16: {}
- Could not convert subsection_index to u16: {}
- Could not convert subsections_number to u16: {}
- Could not convert blob witness_index to u16: {}
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/246b26d580b7a5f3.
Report an issue: GitHub.