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

  1. Inspect the file's document JSON (unzip the .rnote and check its JSON) and confirm which keys exist
  2. Add a `layout` entry to the document JSON matching the expected format schema, or regenerate/export the file from a working rnote version
  3. 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

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


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)