flxzt/rnote · error · anyhow::Error
document has no value `layout`.
Error message
document has no value `layout`.
What it means
Thrown in the maj0min13 file-format migration's TryFrom implementation when the deserialized document JSON object has no `layout` key. The migration extracts required keys (`format`, `background`, `layout`) via `remove()` and fails fast if any are absent, because a valid .rnote file of this format version must contain them to reconstruct the engine snapshot.
Solutions
- Inspect the file's document JSON (unzip the .rnote and check its JSON) and confirm which keys exist
- Add a `layout` entry to the document JSON matching the expected format schema, or regenerate/export the file from a working rnote version
- Wrap the conversion in error handling and surface a clear 'file is corrupt or unsupported version' message to the user instead of panicking
Example fix
// before
let layout = document
.remove("layout")
.ok_or_else(|| anyhow!("document has no value `layout`."))?;
// after
let layout = match document.remove("layout") {
Some(v) => v,
None => {
log::warn!("document missing `layout`, using default layout");
ijson::IValue::default_layout() // or skip migration with a clear user error
}
}; Defensive patterns
Strategy: validation
Validate before calling
// check the document object before conversion
if document.get("layout").is_none() {
return Err(anyhow!("cannot migrate: document is missing required key `layout`"));
} Type guard
fn has_layout(document: &ijson::IObject) -> bool {
document.get("layout").is_some()
} Prevention
- Validate required document keys (format, background, layout) before running format migrations
- Never hand-edit .rnote archives without validating the JSON afterwards
- Keep original files so a failed migration can fall back to a re-export
When it happens
Trigger: Calling TryFrom on a partially populated RnoteFileMaj0Min13 whose document JSON lacks the `layout` key — e.g. a corrupted/truncated .rnote file, a hand-edited archive missing that entry, or a file written by an even older format version where `layout` did not exist.
Common situations: Opening an old or damaged .rnote file whose document section is incomplete; custom tooling that generated .rnote archives without emitting `layout`; version regressions where the key was renamed or dropped.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- engine snapshot does not contain 'stroke_components'.
- engine snapshot is not a JSON object.
- stroke components is not a JSON array.
- stroke component is not a JSON object.
- stroke component does not contain 'value'.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/a4d43d4576c15a39.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min13.rs:35
let engine_snapshot = value
.engine_snapshot
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("engine snapshot is not a JSON object."))?;
let document = engine_snapshot
.get_mut("document")
.ok_or_else(|| anyhow!("`engine_snapshot` has no value `document`."))?
.as_object_mut()
.ok_or_else(|| anyhow!("`document` is not a JSON object."))?;
let format = document
.remove("format")
.ok_or_else(|| anyhow!("document has no value `format`."))?;
let background = document
.remove("background")
.ok_or_else(|| anyhow!("document has no value `background`."))?;
let layout = document
.remove("layout")
.ok_or_else(|| anyhow!("document has no value `layout`."))?;
// discard `snap_positions`, this config is now global.
document.remove("snap_positions");
let mut document_config = ijson::IObject::new();
document_config.insert("format", format);
document_config.insert("background", background);
document_config.insert("layout", layout);
document.insert("config", document_config);
Ok(Self {
engine_snapshot: value.engine_snapshot,
})
}
}
View on GitHub (pinned to bbc5354502)