flxzt/rnote · error · anyhow::Error

value in `stroke_components` array is not a JSON Object.

Error message

value in `stroke_components` array is not a JSON Object.

What it means

Thrown while iterating the "stroke_components" array when an element is not a JSON object. The migration expects each entry to be an object containing a "value" key it can rewrite, so a non-object element aborts the conversion.

Solutions

  1. Fix each stroke_components element to be a JSON object with a "value" key (or remove invalid entries).
  2. Re-save the file from the app version that produced a valid snapshot.
  3. Restore from backup and avoid manual edits to snapshot internals.

Example fix

// before
"stroke_components": [null]
// after
"stroke_components": [ { "value": null } ]
Defensive patterns

Strategy: validation

Validate before calling

for (i, el) in comps.iter().enumerate() {
    if !el.is_object() {
        return Err(format!("stroke_components[{i}] is not a JSON object"));
    }
}

Type guard

fn all_strokes_are_objects(arr: &[serde_json::Value]) -> bool {
    arr.iter().all(|v| v.is_object() && v.get("value").is_some())
}

Try / catch

// sanitize before migrating
let comps: Vec<serde_json::Value> = comps.into_iter().filter(|v| v.is_object()).collect();

Prevention

When it happens

Trigger: An .rnote file where store_snapshot["stroke_components"] is an array but at least one element is a string, number, null, or array rather than an object.

Common situations: Corrupted files from partial writes, hand edits inserting raw values, or a custom generator emitting the wrong element shape.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08). Data as JSON: /api/errors/b220de10abba784c. Report an issue: GitHub.

Appendix: source

Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs:33

    #[serde(rename = "store_snapshot")]
    pub(crate) store_snapshot: ijson::IValue,
}

impl TryFrom<RnoteFileMaj0Min5Patch8> for RnoteFileMaj0Min5Patch9 {
    type Error = anyhow::Error;

    fn try_from(mut file: RnoteFileMaj0Min5Patch8) -> Result<RnoteFileMaj0Min5Patch9, Self::Error> {
        let stroke_components = file
            .store_snapshot
            .get_mut("stroke_components")
            .ok_or_else(|| anyhow!("no value `stroke_components` in `store_snapshot`"))?
            .as_array_mut()
            .ok_or_else(|| anyhow!("value `stroke_components` is not a JSON array."))?;

        for value in stroke_components {
            let stroke = value
                .as_object_mut()
                .ok_or_else(|| anyhow!("value in `stroke_components` array is not a JSON Object."))?
                .get_mut("value")
                .ok_or_else(|| {
                    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>(

View on GitHub (pinned to bbc5354502)