flxzt/rnote · error · anyhow::Error
affine not an array.
Error message
affine not an array.
What it means
Thrown by convert_transform_to_affine_in_obj during the maj0min15 file-format migration when a transform JSON object's "affine" key exists but is not a JSON array. The migration needs to read the 6 affine matrix elements by index, so it requires an array. Rnote files carry transforms serialized as JSON inside the document; a malformed or hand-edited file triggers this.
Solutions
- Inspect the offending JSON at the reported path and change "affine" to a JSON array of 6 numbers, e.g. [1,0,0,1,0,0].
- Regenerate the file from a working version of the app or from a backup instead of repairing it manually.
- Fix the generating code so it serializes affine as an array (serde serializes a [f64;6]/Vec as a JSON array).
Example fix
// before (invalid file JSON)
"transform": { "affine": "1,0,0,1,0,0" }
// after
"transform": { "affine": [1, 0, 0, 1, 0, 0] } Defensive patterns
Strategy: validation
Validate before calling
let t = doc.get("transform").and_then(|v| v.as_object())?;
if t.get("affine").and_then(|a| a.as_array()).map(|a| a.len() >= 8) != Some(true) {
return Err("invalid transform.affine: expected array of >= 8 numbers");
} Type guard
fn is_valid_affine(v: &serde_json::Value) -> bool {
v.get("affine").and_then(|a| a.as_array())
.map(|a| a.len() >= 8 && a.iter().all(|e| e.is_number()))
.unwrap_or(false)
} Prevention
- Validate .rnote file JSON against the expected schema before running migrations.
- Never hand-edit serialized snapshot JSON; use the app's own save path.
- Keep file producers on the same serializer version as consumers.
When it happens
Trigger: Opening/converting an .rnote file whose JSON contains an object entry with a "transform" object where "affine" is a non-array JSON value (number, string, null, object) instead of a 6+ element array.
Common situations: Hand-edited or programmatically generated .rnote files, truncated/corrupted downloads, or files produced by an incompatible writer version.
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.
- value `stroke_components` is not a JSON array.
- engine snapshot is not a JSON object.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/4d2bc2d7077a74b7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs:131
}
}
/// Converts a "object->transform->nalgebra-affine" to "object->(glamx-)affine"
fn convert_transform_to_affine_in_obj(obj: &mut IValue) -> anyhow::Result<()> {
let obj = obj
.as_object_mut()
.ok_or(anyhow!("supplied value is not a JSON object."))?;
let transform = obj
.remove("transform")
.ok_or(anyhow!("rect does not contain 'transform'."))?;
let transform = transform
.as_object()
.ok_or(anyhow!("transform is not a JSON object."))?;
let affine = transform
.get("affine")
.ok_or(anyhow!("transform does not contain 'affine'."))?
.as_array()
.ok_or(anyhow!("affine not an array."))?;
obj.insert(
"affine",
vec![
#[allow(clippy::get_first)]
affine
.get(0)
.cloned()
.ok_or(anyhow!("affine does not have value at index 0"))?,
affine
.get(1)
.cloned()
.ok_or(anyhow!("affine does not have value at index 1"))?,
affine
.get(3)
.cloned()
.ok_or(anyhow!("affine does not have value at index 3"))?,
affineView on GitHub (pinned to bbc5354502)