{"record":{"id":"9def373296b9bf24","repo":"GraphiteEditor/Graphite","slug":"failed-to-rotate-the-render-context-to-the-specifi","errorCode":null,"errorMessage":"Failed to rotate the render context to the specified angle","messagePattern":"Failed to rotate the render context to the specified angle","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/overlays/utility_types_web.rs","lineNumber":1070,"sourceCode":"\t\tself.render_context.set_fill_style_canvas_pattern(&pattern);\n\t\tself.render_context.fill();\n\t}\n\n\tpub fn text(&self, text: &str, font_color: &str, background_color: Option<&str>, transform: DAffine2, padding: f64, pivot: [Pivot; 2]) {\n\t\tlet metrics = self.render_context.measure_text(text).expect(\"Failed to measure the text dimensions\");\n\t\tlet x = match pivot[0] {\n\t\t\tPivot::Start => padding,\n\t\t\tPivot::Middle => -(metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left()) / 2.,\n\t\t\tPivot::End => -padding - metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left(),\n\t\t};\n\t\tlet y = match pivot[1] {\n\t\t\tPivot::Start => padding + metrics.font_bounding_box_ascent() - metrics.font_bounding_box_descent(),\n\t\t\tPivot::Middle => (metrics.font_bounding_box_ascent() + metrics.font_bounding_box_descent()) / 2.,\n\t\t\tPivot::End => -padding,\n\t\t};\n\n\t\tlet [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();\n\t\tself.render_context.set_transform(a, b, c, d, e, f).expect(\"Failed to rotate the render context to the specified angle\");\n\n\t\tif let Some(background) = background_color {\n\t\t\tself.render_context.set_fill_style_str(background);\n\t\t\tself.render_context.fill_rect(\n\t\t\t\t-padding,\n\t\t\t\tpadding,\n\t\t\t\tmetrics.actual_bounding_box_right() - metrics.actual_bounding_box_left() + padding * 2.,\n\t\t\t\tmetrics.font_bounding_box_descent() - metrics.font_bounding_box_ascent() - padding * 2.,\n\t\t\t);\n\t\t}\n\n\t\tself.render_context.set_font(r#\"12px \"Source Sans Pro\", Arial, sans-serif\"#);\n\t\tself.render_context.set_fill_style_str(font_color);\n\t\tself.render_context.fill_text(text, 0., 0.).expect(\"Failed to draw the text at the calculated position\");\n\t\tself.render_context.reset_transform().expect(\"Failed to reset the render context transform\");\n\t}\n\n\tpub fn translation_box(&mut self, translation: DVec2, quad: Quad, typed_string: Option<String>) {","sourceCodeStart":1052,"sourceCodeEnd":1088,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/overlays/utility_types_web.rs#L1052-L1088","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nself.render_context.set_transform(a, b, c, d, e, f).expect(\"Failed to rotate the render context to the specified angle\");\n\n// after\nif ![a, b, c, d, e, f].iter().all(|v| v.is_finite()) {\n\tlog::error!(\"Non-finite overlay text transform\");\n\treturn;\n}\nlet _ = self.render_context.set_transform(a, b, c, d, e, f);","handlingStrategy":"validation","validationCode":"let [a, b, c, d, e, f] = matrix.to_cols_array();\nif ![a, b, c, d, e, f].iter().all(|v| v.is_finite()) {\n\tlog::error!(\"skipping overlay text with non-finite transform\");\n\treturn;\n}","typeGuard":"fn is_finite_affine(m: glam::DAffine2) -> bool {\n\tm.to_cols_array().iter().all(|v| v.is_finite())\n}","tryCatchPattern":"if let Err(js_err) = self.render_context.set_transform(a, b, c, d, e, f) {\n\tlog::error!(\"set_transform failed ({a},{b},{c},{d},{e},{f}): {js_err:?}\");\n\treturn;\n}","preventionTips":["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"],"tags":["rust","wasm-bindgen","canvas-2d","transform-matrix","nan"],"backgroundTag":"non-finite-transform-matrix","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}