flxzt/rnote · error · anyhow::Error

rect does not contain 'transform'.

Error message

rect does not contain 'transform'.

What it means

Thrown in convert_transform_to_affine_in_obj when the supplied object has no 'transform' key. The migration's purpose is to replace the legacy nested transform.affine array with a flat 'affine' array; without 'transform' there is nothing to convert, so the maj0min13 -> maj0min15 upgrade fails. (The message says 'rect' but applies to any object passed to the helper.)

Solutions

  1. Check whether the node already has a flat 'affine' key (already migrated) and make the migration idempotent: skip conversion if 'transform' is absent but 'affine' exists.
  2. Restore the original maj0min13 file from a backup and migrate it once, cleanly.
  3. Re-insert the legacy 'transform' object in the file JSON if the source of truth is known.
  4. Soften the helper to treat a missing 'transform' as a no-op with a warning instead of an error.

Example fix

// before: hard failure
let transform = obj.remove("transform").ok_or(anyhow!("rect does not contain 'transform'."))?;
// after: idempotent migration
if obj.get("affine").is_some() { return Ok(()); } // already converted
let Some(transform) = obj.remove("transform") else {
    log::warn!("no 'transform' to convert; skipping");
    return Ok(());
};
Defensive patterns

Strategy: fallback

Validate before calling

if node.get("transform").is_none() && node.get("affine").is_some() {
    // already migrated; skip
}

Type guard

fn needs_transform_conversion(obj: &IValue) -> bool {
    obj.get("transform").is_some() && obj.get("affine").is_none()
}

Try / catch

if let Err(e) = convert_transform_to_affine_in_obj(rect) {
    if e.to_string().contains("does not contain 'transform'") {
        // likely already migrated; continue instead of failing
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Migrating a file where a rectangle/textstroke/shape node exists but lacks the legacy 'transform' object (e.g. already migrated, partially migrated, or never had one).

Common situations: Files that were partially migrated (transform already removed but 'affine' not inserted); hand-edited JSON; nodes created by tools targeting a newer format being run through the old-format migration.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08). Data as JSON: /api/errors/192d878a598d4e7d. Report an issue: GitHub.

Appendix: source

Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs:123

            }
        }

        //dbg!(&value);

        Ok(Self {
            engine_snapshot: value.engine_snapshot,
        })
    }
}

/// 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

View on GitHub (pinned to bbc5354502)