{"record":{"id":"4d2bc2d7077a74b7","repo":"flxzt/rnote","slug":"affine-not-an-array","errorCode":null,"errorMessage":"affine not an array.","messagePattern":"affine not an array\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs","lineNumber":131,"sourceCode":"    }\n}\n\n/// Converts a \"object->transform->nalgebra-affine\" to \"object->(glamx-)affine\"\nfn convert_transform_to_affine_in_obj(obj: &mut IValue) -> anyhow::Result<()> {\n    let obj = obj\n        .as_object_mut()\n        .ok_or(anyhow!(\"supplied value is not a JSON object.\"))?;\n    let transform = obj\n        .remove(\"transform\")\n        .ok_or(anyhow!(\"rect does not contain 'transform'.\"))?;\n    let transform = transform\n        .as_object()\n        .ok_or(anyhow!(\"transform is not a JSON object.\"))?;\n    let affine = transform\n        .get(\"affine\")\n        .ok_or(anyhow!(\"transform does not contain 'affine'.\"))?\n        .as_array()\n        .ok_or(anyhow!(\"affine not an array.\"))?;\n\n    obj.insert(\n        \"affine\",\n        vec![\n            #[allow(clippy::get_first)]\n            affine\n                .get(0)\n                .cloned()\n                .ok_or(anyhow!(\"affine does not have value at index 0\"))?,\n            affine\n                .get(1)\n                .cloned()\n                .ok_or(anyhow!(\"affine does not have value at index 1\"))?,\n            affine\n                .get(3)\n                .cloned()\n                .ok_or(anyhow!(\"affine does not have value at index 3\"))?,\n            affine","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs#L113-L149","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-edited or programmatically generated .rnote files, truncated/corrupted downloads, or files produced by an incompatible writer version.","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)."],"exampleFix":"// before (invalid file JSON)\n\"transform\": { \"affine\": \"1,0,0,1,0,0\" }\n// after\n\"transform\": { \"affine\": [1, 0, 0, 1, 0, 0] }","handlingStrategy":"validation","validationCode":"let t = doc.get(\"transform\").and_then(|v| v.as_object())?;\nif t.get(\"affine\").and_then(|a| a.as_array()).map(|a| a.len() >= 8) != Some(true) {\n    return Err(\"invalid transform.affine: expected array of >= 8 numbers\");\n}","typeGuard":"fn is_valid_affine(v: &serde_json::Value) -> bool {\n    v.get(\"affine\").and_then(|a| a.as_array())\n        .map(|a| a.len() >= 8 && a.iter().all(|e| e.is_number()))\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["json","file-migration","type-mismatch"],"backgroundTag":"incompatible-source-type","analyzedSha":"bbc5354502ba2fc83eec2670b535348825e679a6","analyzedAt":"2026-09-08T13:20:33.747Z","contentChangedAt":"2026-09-08T13:20:33.747Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}