GraphiteEditor/Graphite · error
Failed to rotate the render context to the specified angle
Error message
Failed to rotate the render context to the specified angle
What it means
text() composes viewport scale * transform * translation into a 6-value matrix and calls set_transform(a, b, c, d, e, f).expect("Failed to rotate the render context to the specified angle"). If any component is NaN or infinite (the usual culprit: a NaN viewport scale or corrupted layer transform leaking into the matrix), engines may throw instead of silently ignoring it, and wasm-bindgen turns that into Err(JsValue) which this expect panics on. The message mentions rotation because the matrix includes the layer's rotation, but the real trigger is non-finite matrix components.
Source
Thrown at editor/src/messages/portfolio/document/overlays/utility_types_web.rs:1070
self.render_context.set_fill_style_canvas_pattern(&pattern);
self.render_context.fill();
}
pub fn text(&self, text: &str, font_color: &str, background_color: Option<&str>, transform: DAffine2, padding: f64, pivot: [Pivot; 2]) {
let metrics = self.render_context.measure_text(text).expect("Failed to measure the text dimensions");
let x = match pivot[0] {
Pivot::Start => padding,
Pivot::Middle => -(metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left()) / 2.,
Pivot::End => -padding - metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left(),
};
let y = match pivot[1] {
Pivot::Start => padding + metrics.font_bounding_box_ascent() - metrics.font_bounding_box_descent(),
Pivot::Middle => (metrics.font_bounding_box_ascent() + metrics.font_bounding_box_descent()) / 2.,
Pivot::End => -padding,
};
let [a, b, c, d, e, f] = (DAffine2::from_scale(DVec2::splat(self.viewport.scale())) * transform * DAffine2::from_translation(DVec2::new(x, y))).to_cols_array();
self.render_context.set_transform(a, b, c, d, e, f).expect("Failed to rotate the render context to the specified angle");
if let Some(background) = background_color {
self.render_context.set_fill_style_str(background);
self.render_context.fill_rect(
-padding,
padding,
metrics.actual_bounding_box_right() - metrics.actual_bounding_box_left() + padding * 2.,
metrics.font_bounding_box_descent() - metrics.font_bounding_box_ascent() - padding * 2.,
);
}
self.render_context.set_font(r#"12px "Source Sans Pro", Arial, sans-serif"#);
self.render_context.set_fill_style_str(font_color);
self.render_context.fill_text(text, 0., 0.).expect("Failed to draw the text at the calculated position");
self.render_context.reset_transform().expect("Failed to reset the render context transform");
}
pub fn translation_box(&mut self, translation: DVec2, quad: Quad, typed_string: Option<String>) {View on GitHub (pinned to c507b35645)
Solutions
- Validate all six matrix components with f64::is_finite before calling set_transform, and skip the label if any fail
- Trace the NaN to its source (viewport scale computation or DAffine2 math) and clamp/repair it there
- Handle the Result with logging instead of expect so one bad matrix cannot abort rendering
Example fix
// before
self.render_context.set_transform(a, b, c, d, e, f).expect("Failed to rotate the render context to the specified angle");
// after
if ![a, b, c, d, e, f].iter().all(|v| v.is_finite()) {
log::error!("Non-finite overlay text transform");
return;
}
let _ = self.render_context.set_transform(a, b, c, d, e, f); Defensive patterns
Strategy: validation
Validate before calling
let [a, b, c, d, e, f] = matrix.to_cols_array();
if ![a, b, c, d, e, f].iter().all(|v| v.is_finite()) {
log::error!("skipping overlay text with non-finite transform");
return;
} Type guard
fn is_finite_affine(m: glam::DAffine2) -> bool {
m.to_cols_array().iter().all(|v| v.is_finite())
} Try / catch
if let Err(js_err) = self.render_context.set_transform(a, b, c, d, e, f) {
log::error!("set_transform failed ({a},{b},{c},{d},{e},{f}): {js_err:?}");
return;
} Prevention
- Validate matrix components with is_finite before every set_transform in drawing code
- Clamp viewport zoom/scale so extreme values cannot become NaN
- When a NaN transform is detected, log the source transform and viewport scale to locate the producer
When it happens
Trigger: Drawing overlay text while viewport.scale(), the layer transform, or the computed pivot offsets contain NaN/Infinity, e.g. a degenerate zoom (0 divided somewhere) or a division by zero in transform decomposition.
Common situations: Zooming to extreme levels producing NaN in scale; transforms built from inverted singular matrices; a document with corrupt layer data feeding NaN into the overlay path.
Related errors
- Failed to draw arc
- Failed to get canvas context
- Failed to measure the text dimensions
- Failed to draw the text at the calculated position
- Failed to draw ellipse
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/9def373296b9bf24.
Report an issue: GitHub.