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
- Fix or regenerate the malformed .xopp file; verify the stroke element has width values for each point
- Pre-validate the parsed XoppStroke and skip/warn on empty widths instead of failing the whole import
- Give single-point strokes a synthetic width (dot) before conversion
- 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
- Validate .xopp files (each point has a matching width) before import
- Skip-and-log degenerate strokes instead of failing the whole document import
- Guard against hand-edited or crash-truncated XOPP files with an XML schema check
- Test the importer against XOPP files from multiple Xournal++ versions
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
- Could not generate pen path from coordinates vector
- Failed to get filename from rnote_file
- failed to parse width attribute of XoppPage for node with id
- failed to parse height attribute of XoppPage with node id
- Err while parsing `style` attribute of XoppBackground
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)