flxzt/rnote · error

failed to parse `width` attribute in XoppStroke with node id

Error message

failed to parse `width` attribute in XoppStroke with node id {:?}, could not find attribute

What it means

Thrown by XoppStroke::load_from_xml when a `<stroke>` element has no `width` attribute. Width is required because rnote consumes it (space-separated f64 values) as pressure/width data for the stroke; absence aborts parsing with this error naming the XML node id.

Solutions

  1. Add width to the stroke element, e.g. width="2.26" (or the space-separated width list Xournal++ writes).
  2. Regenerate the file with the producer writing width for every stroke.
  3. Re-save the file via Xournal++ to normalize required attributes and retry import.

Example fix

// before (file XML)
<stroke tool="pen" color="black">...</stroke>
// after (file XML)
<stroke tool="pen" width="2.26" color="black">...</stroke>
Defensive patterns

Strategy: validation

Validate before calling

fn stroke_elements_have_width(xml: &str) -> Result<(), String> {
    for (i, chunk) in xml.split("<stroke").skip(1).enumerate() {
        let head = chunk.split('>').next().unwrap_or("");
        if !head.contains("width=") {
            return Err(format!("stroke #{i} missing `width` attribute"));
        }
    }
    Ok(())
}

Type guard

fn stroke_attrs_ok(attrs: &[(&str, &str)]) -> bool {
    attrs.iter().any(|(k, _)| *k == "width")
}

Try / catch

match import_xopp(path) {
    Err(e) if e.to_string().contains("`width` attribute in XoppStroke") => {
        eprintln!("A stroke element is missing width; repair XML or re-save in Xournal++: {e:#}");
    }
    Err(e) => eprintln!("import failed: {e:#}"),
    Ok(doc) => { /* proceed */ }
}

Prevention

When it happens

Trigger: Importing a .xopp file whose `<stroke>` element lacks the `width` attribute, e.g. generated by a script, hand-edited, or produced by a tool writing incomplete stroke definitions.

Common situations: Custom xopp generators omitting width; manual XML edits; truncated files where trailing attributes are lost.

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/7e4a637001b18b8e. Report an issue: GitHub.

Appendix: source

Thrown at crates/rnote-engine/src/fileformats/xoppformat.rs:738

        self.color =
            XoppColor::from_strokecolor_attr_value(node.attribute("color").ok_or_else(|| {
                anyhow::anyhow!(
                    "failed to parse `color` attribute in XoppStroke with node id {:?}, could not find attribute",
                    node.id()
                )
            })?)?;

        self.fill = if let Some(fill) = node.attribute("fill") {
            Some(fill.parse::<i32>()?)
        } else {
            None
        };

        self.width = node
            .attribute("width")
            .ok_or_else(|| {
                anyhow::anyhow!(
                    "failed to parse `width` attribute in XoppStroke with node id {:?}, could not find attribute",
                    node.id()
                )
            })?
            .split(' ')
            .filter_map(|split| split.parse::<f64>().ok())
            .collect::<Vec<f64>>();

        self.timestamp = if let Some(_ts) = node.attribute("ts") {
            // the timestamp parsing is fallible and currently not implemented
            // ts.parse::<u64>().ok()
            None
        } else {
            None
        };

        self.audio_filename = node
            .attribute("fn")

View on GitHub (pinned to bbc5354502)