flxzt/rnote · error · anyhow::Error
shapestroke is not a JSON object.
Error message
shapestroke is not a JSON object.
What it means
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.
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.
Example fix
// before
{"value": {"shapestroke": ["rect"]}}
// after
{"value": {"shapestroke": {"shape": {"rect": {"transform": {"affine": [1,0,0,1,0,0,0,0,1]}}}}}} Defensive patterns
Strategy: validation
Validate before calling
fn shapestroke_is_object(comp: &serde_json::Value) -> bool {
comp.get("value")
.and_then(|v| v.get("shapestroke"))
.map_or(true, |s| s.is_object())
} Type guard
fn get_shapestroke<'a>(value: &'a serde_json::Value) -> Option<&'a serde_json::Map<String, serde_json::Value>> {
value.get("shapestroke").and_then(|s| s.as_object())
} Try / catch
match RnoteFileMaj0Min15::try_from(file) {
Ok(f) => open(f),
Err(e) if e.to_string().contains("shapestroke is not a JSON object") => {
log::error!("malformed shapestroke payload: {e}");
restore_from_backup();
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: try_from hits a component with value['shapestroke'] present but not a JSON object (e.g. shapestroke: "rect-data" or shapestroke: []).
Common situations: Hand-edited .rnote archives, files damaged by partial writes, or output of a buggy third-party exporter that flattened shapestroke to a non-object.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- value is not a JSON object.
- shape is not a JSON object.
- vectorimage is not a JSON object.
- stroke value is not a JSON Object.
- brushstroke is not a JSON object.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/2316231298a45d5a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs:47
.ok_or(anyhow!("stroke components is not a JSON array."))?
.iter_mut()
{
let value = comp
.as_object_mut()
.ok_or(anyhow!("stroke component is not a JSON object."))?
.get_mut("value")
.ok_or(anyhow!("stroke component does not contain 'value'."))?;
if value.is_null() {
continue;
}
if let Some(shapestroke) = value
.as_object_mut()
.ok_or(anyhow!("value is not a JSON object."))?
.get_mut("shapestroke")
{
let shape = shapestroke
.as_object_mut()
.ok_or(anyhow!("shapestroke is not a JSON object."))?
.get_mut("shape")
.ok_or(anyhow!("shapestroke does not contain 'shape'."))?;
if let Some(rect) = shape
.as_object_mut()
.ok_or(anyhow!("shape is not a JSON object."))?
.get_mut("rect")
{
convert_transform_to_affine_in_obj(rect)?;
} else if let Some(ellipse) = shape
.as_object_mut()
.ok_or(anyhow!("shape is not a JSON object."))?
.get_mut("ellipse")
{
convert_transform_to_affine_in_obj(ellipse)?;
}
} else if let Some(textstroke) = value
.as_object_mut()View on GitHub (pinned to bbc5354502)