{"record":{"id":"2e427767a7719186","repo":"flxzt/rnote","slug":"brushstroke-is-not-a-json-object","errorCode":null,"errorMessage":"brushstroke is not a JSON object.","messagePattern":"brushstroke is not a JSON object\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs","lineNumber":50,"sourceCode":"                .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,\n                        SegmentMaj0Min5Patch8::CubBez { start, .. } => start,\n                    };\n                    path_upgraded.insert(String::from(\"start\"), ijson::to_value(start)?);\n                    let mut segments_upgraded = ijson::IArray::new();\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs#L32-L68","documentation":"During the maj0min5patch8 -> maj0min5patch9 migration, when a stroke value contains a `brushstroke` key, that value must be a JSON object so the migration can extract and upgrade its `path`. This error is thrown when the `brushstroke` value is a non-object JSON value.","triggerScenarios":"Calling `RnoteFileMaj0Min5Patch9::try_from` on a file where `stroke_components[i].value.brushstroke` is a string, number, array, or boolean instead of an object.","commonSituations":"Corrupted or hand-edited .rnote files; files written by experimental/forked rnote builds that serialized brushstroke differently.","solutions":["Unzip the .rnote file and confirm `brushstroke` is a JSON object with a `path` key.","Restore the document from a backup or autosave copy.","Re-save the file with a matching rnote version so brushstroke is serialized correctly.","If editing manually, restructure the entry as `{ \"brushstroke\": { \"path\": [...], ... } }`."],"exampleFix":"// before\n{ \"value\": { \"brushstroke\": \"corrupted\" } }\n// after\n{ \"value\": { \"brushstroke\": { \"path\": [] } } }","handlingStrategy":"type-guard","validationCode":"// validate before migration\nfn brushstrokes_are_objects(doc: &serde_json::Value) -> bool {\n    doc[\"store_snapshot\"][\"stroke_components\"]\n        .as_array()\n        .map(|arr| arr.iter().all(|v|\n            v[\"value\"].get(\"brushstroke\").map_or(true, |b| b.is_object())))\n        .unwrap_or(false)\n}","typeGuard":"fn brushstroke_is_object(entry: &serde_json::Value) -> bool {\n    entry[\"value\"][\"brushstroke\"]\n        .as_object()\n        .map(|b| b.contains_key(\"path\"))\n        .unwrap_or(false)\n}","tryCatchPattern":"match RnoteFileMaj0Min5Patch9::try_from(file) {\n    Ok(migrated) => /* use migrated */,\n    Err(e) if e.to_string().contains(\"brushstroke is not a JSON object\") => {\n        // drop or rebuild the malformed brushstroke entry\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Ensure brushstroke payloads are always serialized as objects containing `path`.","Do not let third-party tools rewrite stroke JSON without round-trip validation.","Validate the file with a schema check before attempting format migration.","Keep an untouched original copy before any manual repair."],"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"}