flxzt/rnote · error

finishing piet context failed, Err

Error message

finishing piet context failed, Err: {e:?}

What it means

In image generation via cairo, the piet CairoRenderContext's finish() failed after running the caller-supplied draw function. finish() flushes and validates all piet rendering state; an error here means the draw function left the piet context in a bad state or the underlying cairo backend reported an error, so no valid image could be produced.

Solutions

  1. Inspect the wrapped error `{e:?}` to find which piet/cairo operation failed inside draw_func.
  2. Validate stroke geometry (no NaN/infinite coordinates, sane transform) before drawing.
  3. Ensure the draw function completes all piet operations and does not misuse save/restore or clips.
  4. Update piet-cairo/cairo crates in case of a backend bug.

Example fix

// before
draw_func(&mut piet_cx)?;
piet_cx.finish().map_err(|e| anyhow!("finishing piet context failed, Err: {e:?}"))?;
// after
draw_func(&mut piet_cx).context("draw function failed")?;
piet_cx.finish().with_context(|| format!("finishing piet context failed, Err: {e:?}"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: ensure bounds and scale are sane before generating the image
fn gen_params_ok(bounds: kurbo::Rect, scale: f64) -> bool {
    bounds.is_finite() && bounds.width() > 0.0 && bounds.height() > 0.0 && scale.is_finite() && scale > 0.0
}

Type guard

fn finite_bounds(r: &kurbo::Rect) -> bool {
    r.x0.is_finite() && r.x1.is_finite() && r.y0.is_finite() && r.y1.is_finite()
}

Try / catch

match Image::gen_with_piet(draw_fn, bounds, scale) {
    Ok(img) => ..., 
    Err(e) => log::error!("image generation failed: {e:#}"),
}

Prevention

When it happens

Trigger: Calling the public gen_with_piet entry with a draw_func that triggers a cairo error (e.g. invalid path/clip state, unsupported operations) or leaves the piet context unflushed/invalid; cairo surface errors surfacing at finish().

Common situations: Rendering a stroke whose geometry produces an invalid cairo path; exporting a bitmap of a document containing strokes that hit cairo backend limits; bugs in custom draw functions passed to gen_with_piet.

Related errors


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

Appendix: source

Thrown at crates/rnote-engine/src/image.rs:385

            pixel_width: width_scaled,
            pixel_height: height_scaled,
            // cairo renders to bgra8-premultiplied, but we convert it to rgba8-premultiplied
            memory_format: ImageMemoryFormat::R8g8b8a8Premultiplied,
        })
    }

    /// Generates an image with a provided closure that draws onto a [piet_cairo::CairoRenderContext].
    pub fn gen_with_piet<F>(draw_func: F, bounds: Aabb, image_scale: f64) -> anyhow::Result<Self>
    where
        F: FnOnce(&mut piet_cairo::CairoRenderContext) -> anyhow::Result<()>,
    {
        let cairo_draw_fn = move |cairo_cx: &cairo::Context| -> anyhow::Result<()> {
            let mut piet_cx = piet_cairo::CairoRenderContext::new(cairo_cx);
            // Apply the draw function
            draw_func(&mut piet_cx)?;
            piet_cx
                .finish()
                .map_err(|e| anyhow::anyhow!("finishing piet context failed, Err: {e:?}"))?;
            Ok(())
        };

        Self::gen_with_cairo(cairo_draw_fn, bounds, image_scale)
    }
}

pub(super) fn convert_image_bgra_to_rgba(_width: u32, _height: u32, mut bytes: Vec<u8>) -> Vec<u8> {
    for src in bytes.as_chunks_mut::<4>().0 {
        let (blue, green, red, alpha) = (src[0], src[1], src[2], src[3]);
        src[0] = red;
        src[1] = green;
        src[2] = blue;
        src[3] = alpha;
    }
    bytes
}

View on GitHub (pinned to bbc5354502)