flxzt/rnote · error

Stroke has empty widths vector.

Error message

Stroke has empty widths vector.

What it means

from_xoppstroke converts an Xournal++ (XOPP) stroke into a BrushStroke. A pen stroke in XOPP carries a per-point widths vector; if it parsed to empty, there is no geometry to build a path from, so the conversion aborts with this error. It indicates a malformed or degenerate stroke in the source file.

Solutions

  1. Fix or regenerate the malformed .xopp file; verify the stroke element has width values for each point
  2. Pre-validate the parsed XoppStroke and skip/warn on empty widths instead of failing the whole import
  3. Give single-point strokes a synthetic width (dot) before conversion
  4. Update rnote / the xopp parser in case the file uses an older XOPP format variant

Example fix

// before
let widths: Vec<f64> = stroke.coords.iter().map(|c| convert(c[1])).collect();
if widths.is_empty() { return Err(...); }
// after
let widths: Vec<f64> = stroke.coords.iter().map(|c| convert(c[1])).collect();
let widths = if widths.is_empty() { vec![fallback_width; stroke.coords.len()] } else { widths };
Defensive patterns

Strategy: validation

Validate before calling

let widths: Vec<f64> = stroke.coords.iter().map(|c| convert_value_dpi(c[1], DPI, target_dpi)).collect();
if widths.is_empty() {
    eprintln!("skipping degenerate xopp stroke with no widths");
    return Ok(None); // skip instead of failing the import
}

Type guard

fn has_widths(s: &XoppStroke) -> bool {
    !s.coords.is_empty() && s.coords.len() == s.coords.iter().filter(|_| true).count() && !s.coords.is_empty()
}

Try / catch

match Stroke::from_xoppstroke(stroke, offset, target_dpi) {
    Ok(s) => Some(s),
    Err(e) if e.to_string().contains("empty widths") => {
        log::warn!("degenerate stroke skipped: {e}");
        None
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Importing a .xopp file where a stroke element has no width entries — e.g. a single-tap pen dot recorded with zero width points, a hand-edited/truncated XML file, or an empty <stroke> element.

Common situations: Importing Xournal++ files produced by very old versions or other tools with divergent stroke serialization; corrupted or partially recovered .xopp files after a crash.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at crates/rnote-engine/src/strokes/stroke.rs:331

        let mut widths: Vec<f64> = stroke
            .width
            .into_iter()
            .map(|w| crate::utils::convert_value_dpi(w, xoppformat::XoppFile::DPI, target_dpi))
            .collect();

        let coords: Vec<Vector2> = stroke
            .coords
            .into_iter()
            .map(|c| {
                Vector2::new(
                    crate::utils::convert_value_dpi(c[0], xoppformat::XoppFile::DPI, target_dpi),
                    crate::utils::convert_value_dpi(c[1], xoppformat::XoppFile::DPI, target_dpi),
                )
            })
            .collect();

        if widths.is_empty() {
            return Err(anyhow::anyhow!("Stroke has empty widths vector."));
        }

        let mut smooth_options = SmoothOptions::default();

        let layer = match stroke.tool {
            xoppformat::XoppTool::Pen => {
                smooth_options.stroke_color = Some(crate::utils::color_from_xopp(stroke.color));
                StrokeLayer::UserLayer(0)
            }
            xoppformat::XoppTool::Highlighter => {
                let mut color = crate::utils::color_from_xopp(stroke.color);
                // the highlighter always has alpha 0.5
                color.a = 0.5;

                smooth_options.stroke_color = Some(color);
                StrokeLayer::Highlighter
            }
            xoppformat::XoppTool::Eraser => {

View on GitHub (pinned to bbc5354502)