{"record":{"id":"c3321e287cd5279a","repo":"flxzt/rnote","slug":"transform-is-not-a-json-object","errorCode":null,"errorMessage":"transform is not a JSON object.","messagePattern":"transform is not a JSON object\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs","lineNumber":126,"sourceCode":"        //dbg!(&value);\n\n        Ok(Self {\n            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\"))?,","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs#L108-L144","documentation":"Thrown in convert_transform_to_affine_in_obj when the removed 'transform' value is not a JSON object. The helper reads transform.affine (a 3x3 nalgebra matrix serialized as a 9-element array), so 'transform' must be an object; any other JSON type aborts the maj0min13 -> maj0min15 migration.","triggerScenarios":"Migrating a file where a node's 'transform' key holds a non-object value (array, string, number) instead of {\"affine\": [...9 numbers...]}.","commonSituations":"Corrupted or hand-edited legacy files; exporters writing transform as a flat array instead of an object wrapper.","solutions":["Fix the file JSON so each 'transform' is an object like {\"affine\": [m00,m01,m02,m10,m11,m12,m20,m21,m22]}.","Restore the note from a backup and re-save it via the app.","Fix the generating tool to serialize the legacy Transform as a struct/object.","Add a type check in the migration that reports the offending component and skips it."],"exampleFix":"// before\nlet transform = transform.as_object().ok_or(anyhow!(\"transform is not a JSON object.\"))?;\n// after: caller-side guard\nif !value[\"transform\"].is_object() {\n    anyhow::bail!(\"corrupt file: transform must be an object\");\n}","handlingStrategy":"type-guard","validationCode":"let t = &node[\"transform\"];\nif !t.is_object() {\n    return Err(\"transform must be an object with an 'affine' array\".into());\n}","typeGuard":"fn is_valid_transform(v: &IValue) -> bool {\n    v.get(\"transform\")\n        .and_then(|t| t.get(\"affine\"))\n        .and_then(|a| a.as_array())\n        .map_or(false, |a| a.len() == 9)\n}","tryCatchPattern":null,"preventionTips":["Serialize the legacy Transform via serde so it is always {affine: [9 numbers]}.","Validate transform shape (object + 9-element affine) before migrating.","Avoid ad-hoc JSON rewrites that unwrap the transform object."],"tags":["json","file-migration","type-mismatch","schema-validation"],"backgroundTag":"schema-validation-failed","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"}