flxzt/rnote · error

Could not generate pen path from coordinates vector

Error message

Could not generate pen path from coordinates vector

What it means

After converting XOPP coordinates and widths into Elements, PenPath::try_from_elements returns None when the iterator yields no valid elements (or the widths were empty, causing zip to produce nothing). The function surfaces this as an anyhow error rather than silently producing an empty stroke.

Solutions

  1. Check that coords.len() == widths.len() and both are non-empty before conversion
  2. Skip or log-and-continue for strokes with zero points during XOPP import
  3. Inspect the .xopp file's stroke element for missing width or position data
  4. If widths were repaired (e.g. from error 141), verify the repair produces one width per coordinate

Example fix

// before
let penpath = PenPath::try_from_elements(
    coords.into_iter().zip(widths).map(|(pos, pressure)| Element::new(pos + offset, pressure)),
).ok_or_else(|| anyhow!("Could not generate pen path..."))?;
// after
if coords.len() != widths.len() || coords.is_empty() {
    return Err(anyhow!("xopp stroke has {} coords but {} widths", coords.len(), widths.len()));
}
Defensive patterns

Strategy: validation

Validate before calling

if coords.is_empty() || widths.is_empty() || coords.len() != widths.len() {
    return Err(anyhow!("cannot build pen path: {} coords, {} widths", coords.len(), widths.len()));
}

Type guard

fn buildable_penpath(coords: &[Point], widths: &[f64]) -> bool {
    !coords.is_empty() && coords.len() == widths.len()
}

Try / catch

match Stroke::from_xoppstroke(stroke, offset, dpi) {
    Ok(res) => res,
    Err(e) if e.to_string().contains("Could not generate pen path") => {
        log::warn!("empty/unbalanced stroke skipped: {e}");
        default_stroke()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: from_xoppstroke called with a stroke whose coords/widths are empty or mismatched in length (zip truncates to the shorter), leaving try_from_elements with zero elements.

Common situations: Importing degenerate or empty <stroke> entries from a .xopp file; mismatched coordinate/width counts after a parser bug or hand-edited file.

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

Appendix: source

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

            // the coordinate widths are relative to the max width
            widths
                .iter_mut()
                .for_each(|coord_width| *coord_width /= max_width);
        } else {
            // If there are no coordinate widths, we fill the widths vector with pressure 1.0 for a constant width stroke.
            widths = (0..coords.len()).map(|_| 1.0).collect();
        };

        smooth_options.stroke_width = stroke_width;

        let penpath = PenPath::try_from_elements(
            coords
                .into_iter()
                .zip(widths)
                .map(|(pos, pressure)| Element::new(pos + offset, pressure)),
        )
        .ok_or_else(|| anyhow::anyhow!("Could not generate pen path from coordinates vector"))?;

        let brushstroke = BrushStroke::from_penpath(penpath, Style::Smooth(smooth_options));

        Ok((Stroke::BrushStroke(brushstroke), layer))
    }

    pub fn from_xoppimage(
        xopp_image: xoppformat::XoppImage,
        offset: Vector2,
        target_dpi: f64,
    ) -> Result<Self, anyhow::Error> {
        let bounds = Aabb::new(
            Vector2::new(
                crate::utils::convert_value_dpi(
                    xopp_image.left,
                    xoppformat::XoppFile::DPI,
                    target_dpi,
                ),

View on GitHub (pinned to bbc5354502)