{"record":{"id":"a4d43d4576c15a39","repo":"flxzt/rnote","slug":"document-has-no-value-layout","errorCode":null,"errorMessage":"document has no value `layout`.","messagePattern":"document has no value `layout`\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min13.rs","lineNumber":35,"sourceCode":"        let engine_snapshot = value\n            .engine_snapshot\n            .as_object_mut()\n            .ok_or_else(|| anyhow::anyhow!(\"engine snapshot is not a JSON object.\"))?;\n        let document = engine_snapshot\n            .get_mut(\"document\")\n            .ok_or_else(|| anyhow!(\"`engine_snapshot` has no value `document`.\"))?\n            .as_object_mut()\n            .ok_or_else(|| anyhow!(\"`document` is not a JSON object.\"))?;\n\n        let format = document\n            .remove(\"format\")\n            .ok_or_else(|| anyhow!(\"document has no value `format`.\"))?;\n        let background = document\n            .remove(\"background\")\n            .ok_or_else(|| anyhow!(\"document has no value `background`.\"))?;\n        let layout = document\n            .remove(\"layout\")\n            .ok_or_else(|| anyhow!(\"document has no value `layout`.\"))?;\n        // discard `snap_positions`, this config is now global.\n        document.remove(\"snap_positions\");\n\n        let mut document_config = ijson::IObject::new();\n        document_config.insert(\"format\", format);\n        document_config.insert(\"background\", background);\n        document_config.insert(\"layout\", layout);\n        document.insert(\"config\", document_config);\n\n        Ok(Self {\n            engine_snapshot: value.engine_snapshot,\n        })\n    }\n}\n","sourceCodeStart":17,"sourceCodeEnd":50,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min13.rs#L17-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet layout = document\n    .remove(\"layout\")\n    .ok_or_else(|| anyhow!(\"document has no value `layout`.\"))?;\n// after\nlet layout = match document.remove(\"layout\") {\n    Some(v) => v,\n    None => {\n        log::warn!(\"document missing `layout`, using default layout\");\n        ijson::IValue::default_layout() // or skip migration with a clear user error\n    }\n};","handlingStrategy":"validation","validationCode":"// check the document object before conversion\nif document.get(\"layout\").is_none() {\n    return Err(anyhow!(\"cannot migrate: document is missing required key `layout`\"));\n}","typeGuard":"fn has_layout(document: &ijson::IObject) -> bool {\n    document.get(\"layout\").is_some()\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["json","migration","file-format","rust"],"backgroundTag":"missing-required-config-field","analyzedSha":"bbc5354502ba2fc83eec2670b535348825e679a6","analyzedAt":"2026-09-08T13:20:33.747Z","contentChangedAt":"2026-09-08T13:20:33.747Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}