flxzt/rnote · error

store snapshot is not a JSON object.

Error message

store snapshot is not a JSON object.

What it means

During the maj0min5patch9 -> maj0min6 rnote file migration, the file's `store_snapshot` must be a JSON object because the migration needs to move its keys (`stroke_components`, `chrono_components`, `chrono_counter`) into the new top-level `engine_snapshot`. This error is thrown when `store_snapshot` is a non-object JSON value.

Solutions

  1. Unzip the .rnote file and confirm `store_snapshot` is a JSON object containing `stroke_components`, `chrono_components`, and `chrono_counter`.
  2. Restore the file from a backup or autosave copy.
  3. Re-save the document with the rnote version matching its stored format version.
  4. If editing manually, restructure the file as `{ "store_snapshot": { ... }, "document": {...} }`.

Example fix

// before
"store_snapshot": "corrupted"
// after
"store_snapshot": { "stroke_components": [], "chrono_components": [], "chrono_counter": 0 }
Defensive patterns

Strategy: type-guard

Validate before calling

// validate before migration
fn store_snapshot_is_object(doc: &serde_json::Value) -> bool {
    doc.get("store_snapshot").map_or(false, |s| s.is_object())
}

Type guard

fn store_snapshot_object(doc: &serde_json::Value) -> Option<&serde_json::Map<String, serde_json::Value>> {
    doc.get("store_snapshot")?.as_object()
}

Try / catch

match RnoteFileMaj0Min6::try_from(prev) {
    Ok(migrated) => /* use migrated */,
    Err(e) if e.to_string().contains("store snapshot is not a JSON object") => {
        // restore `store_snapshot` object from backup or rebuild defaults
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `RnoteFileMaj0Min6::try_from(RnoteFileMaj0Min5Patch9)` on a file whose `store_snapshot` field is a string, array, number, or other non-object JSON value.

Common situations: Corrupted or partially written .rnote files; manual edits that replaced the store snapshot object with a scalar value.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08). Data as JSON: /api/errors/d61d41d104beea23. Report an issue: GitHub.

Appendix: source

Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min6.rs:22

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RnoteFileMaj0Min6 {
    /// A snapshot of the engine.
    #[serde(rename = "engine_snapshot")]
    pub engine_snapshot: ijson::IValue,
}

impl TryFrom<RnoteFileMaj0Min5Patch9> for RnoteFileMaj0Min6 {
    type Error = anyhow::Error;

    fn try_from(mut value: RnoteFileMaj0Min5Patch9) -> Result<Self, Self::Error> {
        let mut engine_snapshot = ijson::IObject::new();

        let store_snapshot = value
            .store_snapshot
            .as_object_mut()
            .ok_or_else(|| anyhow!("store snapshot is not a JSON object."))?;

        engine_snapshot.insert(String::from("document"), value.document);
        engine_snapshot.insert(
            String::from("stroke_components"),
            store_snapshot
                .remove("stroke_components")
                .ok_or_else(|| anyhow!("store snapshot has no value `stroke_components`."))?,
        );
        engine_snapshot.insert(
            String::from("chrono_components"),
            store_snapshot
                .remove("chrono_components")
                .ok_or_else(|| anyhow!("store snapshot has no value `chrono_components`."))?,
        );
        engine_snapshot.insert(
            String::from("chrono_counter"),
            store_snapshot
                .remove("chrono_counter")

View on GitHub (pinned to bbc5354502)