{"record":{"id":"2316231298a45d5a","repo":"flxzt/rnote","slug":"shapestroke-is-not-a-json-object","errorCode":null,"errorMessage":"shapestroke is not a JSON object.","messagePattern":"shapestroke is not a JSON object\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs","lineNumber":47,"sourceCode":"            .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.\"))?\n                    .get_mut(\"shape\")\n                    .ok_or(anyhow!(\"shapestroke does not contain 'shape'.\"))?;\n\n                if let Some(rect) = shape\n                    .as_object_mut()\n                    .ok_or(anyhow!(\"shape is not a JSON object.\"))?\n                    .get_mut(\"rect\")\n                {\n                    convert_transform_to_affine_in_obj(rect)?;\n                } else if let Some(ellipse) = shape\n                    .as_object_mut()\n                    .ok_or(anyhow!(\"shape is not a JSON object.\"))?\n                    .get_mut(\"ellipse\")\n                {\n                    convert_transform_to_affine_in_obj(ellipse)?;\n                }\n            } else if let Some(textstroke) = value\n                .as_object_mut()","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs#L29-L65","documentation":"Thrown in TryFrom::<RnoteFileMaj0Min13>::try_from during the maj0min13->maj0min15 migration. When a component's 'value' object contains a 'shapestroke' key, that key must map to a JSON object so its 'shape' field can be located; if 'shapestroke' is a scalar, array, or null-like non-object, migration aborts. It signals malformed shape-stroke data in the file.","triggerScenarios":"try_from hits a component with value['shapestroke'] present but not a JSON object (e.g. shapestroke: \"rect-data\" or shapestroke: []).","commonSituations":"Hand-edited .rnote archives, files damaged by partial writes, or output of a buggy third-party exporter that flattened shapestroke to a non-object.","solutions":["Inspect the component JSON and make 'shapestroke' an object containing 'shape'.","Re-save the file with the Rnote version that produced it, then retry the migration.","Restore from backup/autosave if the archive is corrupted.","If the component is unusable, replace its 'value' with null so the migration skips it."],"exampleFix":"// before\n{\"value\": {\"shapestroke\": [\"rect\"]}}\n// after\n{\"value\": {\"shapestroke\": {\"shape\": {\"rect\": {\"transform\": {\"affine\": [1,0,0,1,0,0,0,0,1]}}}}}}","handlingStrategy":"validation","validationCode":"fn shapestroke_is_object(comp: &serde_json::Value) -> bool {\n    comp.get(\"value\")\n        .and_then(|v| v.get(\"shapestroke\"))\n        .map_or(true, |s| s.is_object())\n}","typeGuard":"fn get_shapestroke<'a>(value: &'a serde_json::Value) -> Option<&'a serde_json::Map<String, serde_json::Value>> {\n    value.get(\"shapestroke\").and_then(|s| s.as_object())\n}","tryCatchPattern":"match RnoteFileMaj0Min15::try_from(file) {\n    Ok(f) => open(f),\n    Err(e) if e.to_string().contains(\"shapestroke is not a JSON object\") => {\n        log::error!(\"malformed shapestroke payload: {e}\");\n        restore_from_backup();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Check value.shapestroke is an object before running the migration.","Do not flatten nested stroke payloads when post-processing .rnote files.","Test migrations on a copy of the file, never the original."],"tags":["json","migration","type-mismatch","file-format"],"backgroundTag":"type-mismatch","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"}