{"record":{"id":"39505b0ea6252e76","repo":"flxzt/rnote","slug":"no-value-stroke-components-in-store-snapshot","errorCode":null,"errorMessage":"no value `stroke_components` in `store_snapshot`","messagePattern":"no value `stroke_components` in `store_snapshot`","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs","lineNumber":26,"sourceCode":"/// Rnote file in version: maj 0 min 5 patch 9.\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub(crate) struct RnoteFileMaj0Min5Patch9 {\n    /// The document.\n    #[serde(rename = \"document\", alias = \"sheet\")]\n    pub(crate) document: ijson::IValue,\n    /// The snapshot of the store.\n    #[serde(rename = \"store_snapshot\")]\n    pub(crate) store_snapshot: ijson::IValue,\n}\n\nimpl TryFrom<RnoteFileMaj0Min5Patch8> for RnoteFileMaj0Min5Patch9 {\n    type Error = anyhow::Error;\n\n    fn try_from(mut file: RnoteFileMaj0Min5Patch8) -> Result<RnoteFileMaj0Min5Patch9, Self::Error> {\n        let stroke_components = file\n            .store_snapshot\n            .get_mut(\"stroke_components\")\n            .ok_or_else(|| anyhow!(\"no value `stroke_components` in `store_snapshot`\"))?\n            .as_array_mut()\n            .ok_or_else(|| anyhow!(\"value `stroke_components` is not a JSON array.\"))?;\n\n        for value in stroke_components {\n            let stroke = value\n                .as_object_mut()\n                .ok_or_else(|| anyhow!(\"value in `stroke_components` array is not a JSON Object.\"))?\n                .get_mut(\"value\")\n                .ok_or_else(|| {\n                    anyhow!(\"no value `value` in JSON object of `stroke_components` array.\")\n                })?;\n\n            if stroke.is_null() {\n                continue;\n            }\n\n            if let Some(brushstroke) = stroke\n                .as_object_mut()","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs#L8-L44","documentation":"Thrown by the TryFrom<RnoteFileMaj0Min5Patch8> migration into RnoteFileMaj0Min5Patch9 when the store_snapshot JSON object has no \"stroke_components\" key. The migration must rewrite each stroke component's inner representation, so the key is mandatory in this version's snapshot schema.","triggerScenarios":"Opening an older .rnote file whose store_snapshot object lacks \"stroke_components\" entirely when the loader runs the maj0-min5-patch9 upgrade step.","commonSituations":"Very old or pre-release .rnote files with a different snapshot schema, files stripped by external tools, or hand-edited documents.","solutions":["Add a \"stroke_components\": [] JSON array to store_snapshot in the file (empty array is accepted).","Open/save the file once in the older app version that writes this key, then retry migration.","Restore the document from a backup that contains the complete store_snapshot."],"exampleFix":"// before (store_snapshot)\n{ \"image_snapshots\": [] }\n// after\n{ \"image_snapshots\": [], \"stroke_components\": [] }","handlingStrategy":"validation","validationCode":"let snap = file_json.get(\"store_snapshot\").and_then(|v| v.as_object())?;\nif !snap.contains_key(\"stroke_components\") {\n    return Err(\"file predates maj0-min5-patch9 schema: missing stroke_components\".into());\n}","typeGuard":"fn has_stroke_components(v: &serde_json::Value) -> bool {\n    v.get(\"store_snapshot\")\n        .and_then(|s| s.get(\"stroke_components\"))\n        .map(|c| c.is_array())\n        .unwrap_or(false)\n}","tryCatchPattern":"// Rust caller migrating files\nmatch RnoteFileMaj0Min5Patch9::try_from(file_maj0min5patch8) {\n    Ok(f) => /* proceed */,\n    Err(e) if e.to_string().contains(\"stroke_components\") => {\n        // re-save with the older app version or restore backup\n    }\n}","preventionTips":["Detect file version before migrating and warn on schema gaps.","Keep old .rnote files backed up before opening with newer app versions.","Test migrations against archived files from every historical format version."],"tags":["json","file-migration","missing-key"],"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"}