unicity-aos/aos-ce · error · serde_json::Error
canonical document exceeds bound
Error message
canonical document exceeds bound
What it means
parse_canonical rejects any input larger than MAX_CANONICAL_DOCUMENT_BYTES before parsing, returning a serde_json::Error wrapping an InvalidData io error with the message "canonical document exceeds bound". The library enforces a hard size cap to guarantee bounded memory use when parsing and canonicalizing untrusted canonical JSON documents.
Solutions
- Measure input.len() before calling and split, compress, or reject oversized documents upstream.
- Check the configured MAX_CANONICAL_DOCUMENT_BYTES constant and, if legitimately too small for your documents, raise it in the source.
- Remove extraneous bytes (trailing whitespace, appended blobs) from the input before parsing.
- Match on the returned serde_json::Error's io kind InvalidData with this message to surface a clear 'payload too large' error to the caller.
Example fix
// before
let value: MyDoc = parse_canonical(&raw)?;
// after
const MAX: usize = MAX_CANONICAL_DOCUMENT_BYTES;
if raw.len() > MAX {
return Err(MyError::PayloadTooLarge(raw.len(), MAX));
}
let value: MyDoc = parse_canonical(&raw)?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_within_bound(input: &[u8]) -> Result<(), String> {
if input.len() > MAX_CANONICAL_DOCUMENT_BYTES {
return Err(format!("input is {} bytes, limit is {}", input.len(), MAX_CANONICAL_DOCUMENT_BYTES));
}
Ok(())
} Try / catch
match parse_canonical::<T>(&input) {
Err(e) if e.io_error_kind() == Some(std::io::ErrorKind::InvalidData) => /* payload too large: report sizes and reject upstream */,
other => other?,
} Prevention
- Check input.len() against MAX_CANONICAL_DOCUMENT_BYTES before every parse call.
- Enforce the same limit at the network/queue boundary so oversized payloads never reach parsing.
- Add a round-trip test with a fixture at the exact size bound to catch limit regressions.
When it happens
Trigger: Calling parse_canonical::<T>() with an input byte slice whose length exceeds MAX_CANONICAL_DOCUMENT_BYTES. Detected before any JSON parsing occurs.
Common situations: Accidentally passing a whole file to parse_canonical when it contains multiple concatenated documents or appended data; a client sending an unexpectedly large canonical payload; test fixtures growing past the limit after schema additions (seen by tests like exact_canonical_fixtures_round_trip and canonical_json_rejects_duplicate_oversized_and_unknown_input).
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- must not be empty
- must be an absolute path
- bundled executable must have a parent directory
- AOS managed path must be a real directory
- cannot contain a platform PATH separator
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/f6f2c9af99948648.
Report an issue: GitHub.
Appendix: source
Thrown at capsules/capsule-surface-model/src/canonical.rs:174
pub fn canonical_bytes<T: Serialize>(value: &T) -> Result<Vec<u8>, serde_json::Error> {
let raw = serde_json::to_vec(value)?;
let parsed = serde_json::from_slice::<CanonicalJson>(&raw).map_err(|error| {
serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::InvalidData, error))
})?;
serde_json::to_vec(&parsed)
}
/// Serialize to deterministic canonical JSON text.
pub fn canonical_string<T: Serialize>(value: &T) -> Result<String, serde_json::Error> {
canonical_bytes(value).map(|bytes| String::from_utf8(bytes).expect("JSON is UTF-8"))
}
/// Parse once for duplicate keys, canonicalize, then deserialize the typed value.
pub fn parse_canonical<T: serde::de::DeserializeOwned>(
input: &[u8],
) -> Result<T, serde_json::Error> {
if input.len() > MAX_CANONICAL_DOCUMENT_BYTES {
return Err(serde_json::Error::io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"canonical document exceeds bound",
)));
}
let parsed = serde_json::from_slice::<CanonicalJson>(input)?;
let canonical = serde_json::to_vec(&parsed)?;
serde_json::from_slice(&canonical)
}
/// Canonical digest for one or more ordered semantic values.
pub fn digest_parts<T: Serialize>(parts: &T) -> Result<String, serde_json::Error> {
Ok(blake3::hash(&canonical_bytes(parts)?).to_hex().to_string())
}
/// Validate a BLAKE3 digest in its lowercase hexadecimal form.
pub fn valid_blake3_digest(value: &str) -> bool {
value.len() == 64
&& valueView on GitHub (pinned to f6f22024fb)