flxzt/rnote · error · anyhow::Error
value `stroke_components` is not a JSON array.
Error message
value `stroke_components` is not a JSON array.
What it means
Thrown by the same maj0min5patch9 TryFrom migration when "stroke_components" exists in store_snapshot but is not a JSON array. The migration iterates the value with as_array_mut() to rewrite each stroke, so anything else fails.
Solutions
- Change "stroke_components" in store_snapshot to a JSON array (wrap the value in [ ] if it was a single object).
- Re-save the file from the app version that produced a valid snapshot, then retry.
- Restore from backup.
Example fix
// before
"stroke_components": { "0": {...} }
// after
"stroke_components": [ { ... } ] Defensive patterns
Strategy: type-guard
Validate before calling
let comps = snap.get("stroke_components")?;
if !comps.is_array() { return Err("stroke_components must be a JSON array".into()); } Type guard
fn is_valid_stroke_components(snap: &serde_json::Value) -> bool {
snap.get("stroke_components").map(|c| c.is_array()).unwrap_or(false)
} Try / catch
match migrate(file) {
Ok(f) => f,
Err(e) if e.to_string().contains("not a JSON array") => {
eprintln!("snapshot schema invalid; restore from backup: {e}");
return Err(e);
}
} Prevention
- Validate snapshot JSON shape before loading files from external tools.
- Avoid manual edits to store_snapshot internals.
- Add schema tests for each historical format version.
When it happens
Trigger: Migrating an .rnote file where store_snapshot["stroke_components"] is an object, string, number, or null instead of an array.
Common situations: Schema drift between very old file versions, hand edits, or external tooling that rewrote the snapshot incorrectly.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- image is not a JSON object.
- supplied value is not a JSON object.
- transform is not a JSON object.
- affine not an array.
- engine snapshot is not a JSON object.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/5de19e7039015b8f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs:28
pub(crate) struct RnoteFileMaj0Min5Patch9 {
/// The document.
#[serde(rename = "document", alias = "sheet")]
pub(crate) document: ijson::IValue,
/// The snapshot of the store.
#[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")View on GitHub (pinned to bbc5354502)