GraphiteEditor/Graphite · error
Failed to transform circle
Error message
Failed to transform circle
What it means
Applies a caller-supplied DAffine2 to the canvas via ctx.transform(a, b, c, d, e, f). The JS transform() throws when the matrix is non-invertible or contains non-finite values (NaN/Inf). The matrix comes from the transform: Option<DAffine2> parameter of dashed_circle; a singular (zero-determinant) or NaN-corrupted affine makes the call throw and the .expect panic, aborting the overlay frame.
Source
Thrown at editor/src/messages/portfolio/document/overlays/utility_types_web.rs:409
pub fn dashed_circle(
&mut self,
position: DVec2,
radius: f64,
color_fill: Option<&str>,
color_stroke: Option<&str>,
dash_width: Option<f64>,
dash_gap_width: Option<f64>,
dash_offset: Option<f64>,
transform: Option<DAffine2>,
) {
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
let position = self.snap_to_physical_pixel(position);
self.start_dpi_aware_transform();
if let Some(transform) = transform {
let [a, b, c, d, e, f] = transform.to_cols_array();
self.render_context.transform(a, b, c, d, e, f).expect("Failed to transform circle");
}
if let Some(dash_width) = dash_width {
let dash_gap_width = dash_gap_width.unwrap_or(1.);
let array = js_sys::Array::new();
array.push(&JsValue::from(dash_width));
array.push(&JsValue::from(dash_gap_width));
if let Some(dash_offset) = dash_offset {
if dash_offset != 0. {
self.render_context.set_line_dash_offset(dash_offset);
}
}
self.render_context
.set_line_dash(&JsValue::from(array))
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
.ok();View on GitHub (pinned to c507b35645)
Solutions
- Validate the affine before applying: all six entries finite and the 2x2 determinant non-zero; fall back to skipping the transform.
- Fix upstream zero-scale computation — guard divisions by viewport size and zoom.
- Swap .expect for a logged if-let Err so a bad matrix cannot crash the frame.
Example fix
// before
self.render_context.transform(a, b, c, d, e, f).expect("Failed to transform circle");
// after
let invertible = [a, b, c, d, e, f].iter().all(|v| v.is_finite()) && a * d - b * c != 0.;
if invertible {
if let Err(err) = self.render_context.transform(a, b, c, d, e, f) {
log::warn!("Failed to apply circle transform: {:?}", err);
}
} Defensive patterns
Strategy: validation
Validate before calling
fn affine_is_drawable(transform: DAffine2) -> bool {
let [a, b, c, d, e, f] = transform.to_cols_array();
[a, b, c, d, e, f].iter().all(|v| v.is_finite()) && a * d - b * c != 0.
} Type guard
fn is_invertible_2d(transform: DAffine2) -> bool {
let det = transform.matrix2.determinant();
det.is_finite() && det != 0.
} Try / catch
if let Err(err) = self.render_context.transform(a, b, c, d, e, f) {
log::warn!("failed to apply canvas transform: {:?}", err);
} Prevention
- Guard divisions by viewport size and zoom so scale can never be zero or NaN
- Validate affine inputs at gizmo/overlay boundaries before handing them to the canvas
- Degrade canvas Result values with logged .ok() instead of .expect in render paths
When it happens
Trigger: Drawing a dashed circle overlay with a transform whose determinant is zero (zero-scaled view) or whose coefficients are NaN — typically computed from a zero-size viewport or a division by zero earlier in the transform stack.
Common situations: Zoom or resize edge cases producing 0/NaN scale; gizmo overlays receiving document transforms that became singular after extreme scaling values.
Related errors
- Failed to draw ellipse
- Failed to draw the circle
- transform should be able to be set to be able to account for
- Failed to draw hover ring
- Failed to draw main ring
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/79d386f37a104c6d.
Report an issue: GitHub.