{"record":{"id":"776752df4b31da31","repo":"flxzt/rnote","slug":"brushstroke-has-no-value-path","errorCode":null,"errorMessage":"brushstroke has no value `path`.","messagePattern":"brushstroke has no value `path`\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs","lineNumber":54,"sourceCode":"                    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\n                    for seg in seg_iter {\n                        let mut segment_upgraded = ijson::IObject::new();\n\n                        match seg {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs#L36-L72","documentation":"In the maj0min5patch8 -> maj0min5patch9 migration, a `brushstroke` object must contain a `path` key, which holds the pen path to be upgraded to the new segmented format. This error is thrown when the brushstroke object exists but `path` is missing after `remove(\"path\")` returns None.","triggerScenarios":"Running `RnoteFileMaj0Min5Patch9::try_from` on a file where `stroke_components[i].value.brushstroke` is an object but lacks the `path` field.","commonSituations":"Files truncated or corrupted during write, files edited by hand with the path stripped, or files from a fork that stored geometry under a different key.","solutions":["Inspect the file JSON and ensure every brushstroke object includes a `path` array.","Restore the document from a backup or autosave.","If the path is unrecoverable, delete that stroke component from the array so the rest of the file loads.","Re-export the document with the matching rnote version to regenerate a valid brushstroke."],"exampleFix":"// before\n{ \"brushstroke\": { \"style\": {...} } }\n// after\n{ \"brushstroke\": { \"path\": [], \"style\": {...} } }","handlingStrategy":"validation","validationCode":"// validate before migration\nfn brushstrokes_have_path(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.get(\"path\").is_some())))\n        .unwrap_or(false)\n}","typeGuard":"fn brushstroke_has_path(entry: &serde_json::Value) -> bool {\n    entry[\"value\"][\"brushstroke\"][\"path\"].is_array()\n}","tryCatchPattern":"match RnoteFileMaj0Min5Patch9::try_from(file) {\n    Ok(migrated) => /* use migrated */,\n    Err(e) if e.to_string().contains(\"brushstroke has no value `path`\") => {\n        // remove the stroke or restore path from backup\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never strip geometry keys from brushstroke objects when editing files.","Validate brushstroke objects contain `path` before saving custom tooling output.","Use backup/autosave copies when attempting repairs.","Re-export from rnote rather than hand-assembling brushstroke JSON."],"tags":["file-format","json","migration","missing-field"],"backgroundTag":"missing-required-argument","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"}