{"record":{"id":"5de19e7039015b8f","repo":"flxzt/rnote","slug":"value-stroke-components-is-not-a-json-array","errorCode":null,"errorMessage":"value `stroke_components` is not a JSON array.","messagePattern":"value `stroke_components` is not a JSON array\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs","lineNumber":28,"sourceCode":"pub(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()\n                .ok_or_else(|| anyhow!(\"stroke value is not a JSON Object.\"))?\n                .get_mut(\"brushstroke\")","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs#L10-L46","documentation":"Thrown by the same maj0min5patch9 TryFrom migration when \"stroke_components\" exists in store_snapshot but is not a JSON array. The migration iterates the value with as_array_mut() to rewrite each stroke, so anything else fails.","triggerScenarios":"Migrating an .rnote file where store_snapshot[\"stroke_components\"] is an object, string, number, or null instead of an array.","commonSituations":"Schema drift between very old file versions, hand edits, or external tooling that rewrote the snapshot incorrectly.","solutions":["Change \"stroke_components\" in store_snapshot to a JSON array (wrap the value in [ ] if it was a single object).","Re-save the file from the app version that produced a valid snapshot, then retry.","Restore from backup."],"exampleFix":"// before\n\"stroke_components\": { \"0\": {...} }\n// after\n\"stroke_components\": [ { ... } ]","handlingStrategy":"type-guard","validationCode":"let comps = snap.get(\"stroke_components\")?;\nif !comps.is_array() { return Err(\"stroke_components must be a JSON array\".into()); }","typeGuard":"fn is_valid_stroke_components(snap: &serde_json::Value) -> bool {\n    snap.get(\"stroke_components\").map(|c| c.is_array()).unwrap_or(false)\n}","tryCatchPattern":"match migrate(file) {\n    Ok(f) => f,\n    Err(e) if e.to_string().contains(\"not a JSON array\") => {\n        eprintln!(\"snapshot schema invalid; restore from backup: {e}\");\n        return Err(e);\n    }\n}","preventionTips":["Validate snapshot JSON shape before loading files from external tools.","Avoid manual edits to store_snapshot internals.","Add schema tests for each historical format version."],"tags":["json","file-migration","type-mismatch"],"backgroundTag":"incompatible-source-type","analyzedSha":"bbc5354502ba2fc83eec2670b535348825e679a6","analyzedAt":"2026-09-08T13:20:33.747Z","contentChangedAt":"2026-09-08T13:20:33.747Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}