{"record":{"id":"64a45c16bf41f5bc","repo":"flxzt/rnote","slug":"stroke-value-is-not-a-json-object","errorCode":null,"errorMessage":"stroke value is not a JSON Object.","messagePattern":"stroke value is not a JSON Object\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs","lineNumber":45,"sourceCode":"            .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\")\n            {\n                let brushstroke = brushstroke\n                    .as_object_mut()\n                    .ok_or_else(|| anyhow!(\"brushstroke is not a JSON object.\"))?;\n                let path = ijson::from_value::<PenPathMaj0Min5Patch8>(\n                    &brushstroke\n                        .remove(\"path\")\n                        .ok_or_else(|| anyhow!(\"brushstroke has no value `path`.\"))?,\n                )?;\n                let mut path_upgraded = ijson::IObject::new();\n                let mut seg_iter = path.inner().into_iter().peekable();\n\n                if let Some(start) = seg_iter.peek() {\n                    let start = match start {\n                        SegmentMaj0Min5Patch8::Dot { element } => element,\n                        SegmentMaj0Min5Patch8::Line { start, .. } => start,\n                        SegmentMaj0Min5Patch8::QuadBez { start, .. } => start,","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs#L27-L63","documentation":"In the maj0min5patch8 -> maj0min5patch9 migration, the inner `value` of a stroke component must itself be a JSON object so that its `brushstroke` key can be inspected. This error is thrown when the unwrapped stroke value is a scalar, string, array, or null-adjacent non-object type instead.","triggerScenarios":"Running `RnoteFileMaj0Min5Patch9::try_from` on a file where a `stroke_components[i].value` entry is not a JSON object, so `.as_object_mut()` fails while probing for `brushstroke`.","commonSituations":"Manually edited or corrupted .rnote files where a stroke `value` was replaced with a plain value, or files produced by a tool writing the wrong nesting level.","solutions":["Inspect the failing entry in the extracted file JSON and ensure `stroke_components[i].value` is a JSON object.","Restore the file from a backup or autosave rather than patching by hand.","Re-export or re-save the document with the rnote version matching the file's stored format version.","If a null/empty stroke was intended, remove the element from the array entirely instead of leaving a non-object."],"exampleFix":"// before\n{ \"value\": \"not-an-object\" }\n// after\n{ \"value\": { \"brushstroke\": { \"path\": [...] } } }","handlingStrategy":"type-guard","validationCode":"// validate before migration\nfn stroke_values_are_objects(doc: &serde_json::Value) -> bool {\n    doc[\"store_snapshot\"][\"stroke_components\"]\n        .as_array()\n        .map(|arr| arr.iter().all(|v| v.get(\"value\").map(|x| x.is_object()).unwrap_or(false)))\n        .unwrap_or(false)\n}","typeGuard":"fn stroke_value_is_object(entry: &serde_json::Value) -> bool {\n    entry.get(\"value\").map(|v| v.is_object()).unwrap_or(false)\n}","tryCatchPattern":"match RnoteFileMaj0Min5Patch9::try_from(file) {\n    Ok(migrated) => /* use migrated */,\n    Err(e) if e.to_string().contains(\"stroke value is not a JSON Object\") => {\n        // skip or repair the offending stroke entry\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep the `{ \"value\": { ... } }` wrapper structure intact when manipulating stroke_components JSON.","Validate document JSON against the format schema before saving or migrating.","Prefer programmatic edits via rnote APIs over raw JSON surgery.","Maintain backups before running format upgrades."],"tags":["file-format","json","migration","type-mismatch"],"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"}