{"record":{"id":"2aa5ce56b652c22e","repo":"flxzt/rnote","slug":"stroke-components-is-not-a-json-array","errorCode":null,"errorMessage":"stroke components is not a JSON array.","messagePattern":"stroke components is not a JSON array\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs","lineNumber":29,"sourceCode":"    pub engine_snapshot: ijson::IValue,\n}\n\nimpl TryFrom<RnoteFileMaj0Min13> for RnoteFileMaj0Min15 {\n    type Error = anyhow::Error;\n\n    fn try_from(mut value: RnoteFileMaj0Min13) -> Result<Self, Self::Error> {\n        let engine_snapshot = value\n            .engine_snapshot\n            .as_object_mut()\n            .ok_or(anyhow!(\"engine snapshot is not a JSON object.\"))?;\n\n        for comp in engine_snapshot\n            .get_mut(\"stroke_components\")\n            .ok_or(anyhow!(\n                \"engine snapshot does not contain 'stroke_components'.\"\n            ))?\n            .as_array_mut()\n            .ok_or(anyhow!(\"stroke components is not a JSON array.\"))?\n            .iter_mut()\n        {\n            let value = comp\n                .as_object_mut()\n                .ok_or(anyhow!(\"stroke component is not a JSON object.\"))?\n                .get_mut(\"value\")\n                .ok_or(anyhow!(\"stroke component does not contain 'value'.\"))?;\n            if value.is_null() {\n                continue;\n            }\n            if let Some(shapestroke) = value\n                .as_object_mut()\n                .ok_or(anyhow!(\"value is not a JSON object.\"))?\n                .get_mut(\"shapestroke\")\n            {\n                let shape = shapestroke\n                    .as_object_mut()\n                    .ok_or(anyhow!(\"shapestroke is not a JSON object.\"))?","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs#L11-L47","documentation":"Thrown when the `stroke_components` entry in the engine snapshot exists but is not a JSON array. The migration iterates the components with `.iter_mut()`, which requires an array, so it validates with `as_array_mut()` first.","triggerScenarios":"Converting an RnoteFileMaj0Min13 whose engine snapshot has `stroke_components` set to an object, string, number, or null instead of an array — usually from corruption or a schema mismatch.","commonSituations":"Files edited by hand or by scripts that replaced the array with a different JSON type; incompatible snapshots from other tools; truncated writes.","solutions":["Unzip the .rnote file and confirm `stroke_components` is a JSON array","Fix the JSON type manually or regenerate the file from the original data","In code, coerce/wrap a non-array value into an array before the migration"],"exampleFix":"// before\n.as_array_mut()\n.ok_or(anyhow!(\"stroke components is not a JSON array.\"))?;\n// after\nmatch engine_snapshot.get_mut(\"stroke_components\").map(|v| v.as_array_mut()) {\n    Some(Some(arr)) => arr,\n    _ => return Err(anyhow!(\"stroke components is not a JSON array.\")),\n}","handlingStrategy":"type-guard","validationCode":"match engine_snapshot.get(\"stroke_components\") {\n    Some(v) if v.as_array().is_some() => {},\n    _ => return Err(anyhow!(\"stroke_components must be a JSON array\")),\n}","typeGuard":"fn stroke_components_is_array(snapshot: &ijson::IObject) -> bool {\n    snapshot.get(\"stroke_components\").and_then(|v| v.as_array()).is_some()\n}","tryCatchPattern":"// narrow the failing step with context\nRnoteFileMaj0Min15::try_from(file_maj0min13)\n    .with_context(|| \"invalid stroke_components type while migrating .rnote file\")?;","preventionTips":["Assert `stroke_components` is an array before iterating","Never mutate .rnote JSON with untyped scripts","Validate file JSON with a schema before loading"],"tags":["json","type-mismatch","migration","rust"],"backgroundTag":"type-mismatch","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"}