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
- Check that coords.len() == widths.len() and both are non-empty before conversion
- Skip or log-and-continue for strokes with zero points during XOPP import
- Inspect the .xopp file's stroke element for missing width or position data
- 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
- Check coords.len() == widths.len() before zipping — zip silently truncates
- Reject or repair empty <stroke> elements at parse time
- Unit-test the XOPP importer with single-point and zero-width strokes
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
- Stroke has empty widths vector.
- Failed to get filename from rnote_file
- Generating doc svg failed, returned None.
- failed to parse width attribute of XoppPage for node with id
- failed to parse height attribute of XoppPage with node id
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)