{"record":{"id":"9a4b08fdc2b203cd","repo":"flxzt/rnote","slug":"transform-does-not-contain-affine","errorCode":null,"errorMessage":"transform does not contain 'affine'.","messagePattern":"transform does not contain 'affine'\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs","lineNumber":129,"sourceCode":"            engine_snapshot: value.engine_snapshot,\n        })\n    }\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()","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs#L111-L147","documentation":"Thrown in convert_transform_to_affine_in_obj when the 'transform' object has no 'affine' key. The migration extracts the 9-element nalgebra affine matrix from transform.affine and rewrites it as a flat 6-element 'affine' array on the parent object; without the key the maj0min13 -> maj0min15 upgrade cannot proceed.","triggerScenarios":"Migrating a file where a node has {\"transform\": {...}} but the transform object lacks 'affine' (e.g. it holds only a rotation/scale sub-object or was truncated).","commonSituations":"Hand-edited legacy files; older/buggy exporters writing transforms without the affine matrix; partially migrated files where affine was already flattened to the parent but 'transform' remained.","solutions":["If the parent already has a flat 'affine', treat the file as migrated and make the migration idempotent (skip when 'affine' already exists).","Re-insert the legacy 'affine' 9-number array into the transform object in the file JSON.","Restore the note from a backup and migrate it again cleanly.","Change the helper to warn-and-skip on missing 'affine' rather than failing the whole file load."],"exampleFix":"// before\n.get(\"affine\").ok_or(anyhow!(\"transform does not contain 'affine'.\"))?\n// after: idempotent + tolerant\nif obj.get(\"affine\").is_some() { return Ok(()); }\nlet Some(affine) = transform.get(\"affine\") else {\n    log::warn!(\"transform missing 'affine'; skipping conversion\");\n    return Ok(());\n};","handlingStrategy":"validation","validationCode":"let t = &node[\"transform\"];\nif t.is_object() && t.get(\"affine\").is_none() {\n    return Err(\"transform missing 'affine'\".into());\n}","typeGuard":"fn has_affine(v: &IValue) -> bool {\n    v.get(\"transform\")\n        .and_then(|t| t.get(\"affine\"))\n        .map_or(false, |a| a.as_array().map_or(false, |a| a.len() == 9))\n}","tryCatchPattern":"if let Err(e) = convert_transform_to_affine_in_obj(rect) {\n    if e.to_string().contains(\"does not contain 'affine'\") {\n        // check for already-flat 'affine' and skip, else restore backup\n    } else { return Err(e); }\n}","preventionTips":["Validate every transform has a 9-element affine array before migration.","Check for an already-converted flat 'affine' to keep migration idempotent.","Keep pre-migration backups so partial migrations can be redone cleanly."],"tags":["json","file-migration","missing-key","transform-matrix"],"backgroundTag":"missing-required-argument","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"}