flxzt/rnote · error
brushstroke has no value `path`.
Error message
brushstroke has no value `path`.
What it means
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.
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.
Example fix
// before
{ "brushstroke": { "style": {...} } }
// after
{ "brushstroke": { "path": [], "style": {...} } } Defensive patterns
Strategy: validation
Validate before calling
// validate before migration
fn brushstrokes_have_path(doc: &serde_json::Value) -> bool {
doc["store_snapshot"]["stroke_components"]
.as_array()
.map(|arr| arr.iter().all(|v|
v["value"].get("brushstroke").map_or(true, |b| b.get("path").is_some())))
.unwrap_or(false)
} Type guard
fn brushstroke_has_path(entry: &serde_json::Value) -> bool {
entry["value"]["brushstroke"]["path"].is_array()
} Try / catch
match RnoteFileMaj0Min5Patch9::try_from(file) {
Ok(migrated) => /* use migrated */,
Err(e) if e.to_string().contains("brushstroke has no value `path`") => {
// remove the stroke or restore path from backup
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: Running `RnoteFileMaj0Min5Patch9::try_from` on a file where `stroke_components[i].value.brushstroke` is an object but lacks the `path` field.
Common situations: 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.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- store snapshot has no value `stroke_components`.
- store snapshot has no value `chrono_components`.
- store snapshot has no value `chrono_counter`.
- document has no value `layout`.
- engine snapshot does not contain 'stroke_components'.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/776752df4b31da31.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs:54
anyhow!("no value `value` in JSON object of `stroke_components` array.")
})?;
if stroke.is_null() {
continue;
}
if let Some(brushstroke) = stroke
.as_object_mut()
.ok_or_else(|| anyhow!("stroke value is not a JSON Object."))?
.get_mut("brushstroke")
{
let brushstroke = brushstroke
.as_object_mut()
.ok_or_else(|| anyhow!("brushstroke is not a JSON object."))?;
let path = ijson::from_value::<PenPathMaj0Min5Patch8>(
&brushstroke
.remove("path")
.ok_or_else(|| anyhow!("brushstroke has no value `path`."))?,
)?;
let mut path_upgraded = ijson::IObject::new();
let mut seg_iter = path.inner().into_iter().peekable();
if let Some(start) = seg_iter.peek() {
let start = match start {
SegmentMaj0Min5Patch8::Dot { element } => element,
SegmentMaj0Min5Patch8::Line { start, .. } => start,
SegmentMaj0Min5Patch8::QuadBez { start, .. } => start,
SegmentMaj0Min5Patch8::CubBez { start, .. } => start,
};
path_upgraded.insert(String::from("start"), ijson::to_value(start)?);
let mut segments_upgraded = ijson::IArray::new();
for seg in seg_iter {
let mut segment_upgraded = ijson::IObject::new();
match seg {View on GitHub (pinned to bbc5354502)