{"record":{"id":"79d386f37a104c6d","repo":"GraphiteEditor/Graphite","slug":"failed-to-transform-circle","errorCode":null,"errorMessage":"Failed to transform circle","messagePattern":"Failed to transform circle","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/overlays/utility_types_web.rs","lineNumber":409,"sourceCode":"\tpub fn dashed_circle(\n\t\t&mut self,\n\t\tposition: DVec2,\n\t\tradius: f64,\n\t\tcolor_fill: Option<&str>,\n\t\tcolor_stroke: Option<&str>,\n\t\tdash_width: Option<f64>,\n\t\tdash_gap_width: Option<f64>,\n\t\tdash_offset: Option<f64>,\n\t\ttransform: Option<DAffine2>,\n\t) {\n\t\tlet color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);\n\t\tlet position = self.snap_to_physical_pixel(position);\n\n\t\tself.start_dpi_aware_transform();\n\n\t\tif let Some(transform) = transform {\n\t\t\tlet [a, b, c, d, e, f] = transform.to_cols_array();\n\t\t\tself.render_context.transform(a, b, c, d, e, f).expect(\"Failed to transform circle\");\n\t\t}\n\n\t\tif let Some(dash_width) = dash_width {\n\t\t\tlet dash_gap_width = dash_gap_width.unwrap_or(1.);\n\t\t\tlet array = js_sys::Array::new();\n\t\t\tarray.push(&JsValue::from(dash_width));\n\t\t\tarray.push(&JsValue::from(dash_gap_width));\n\n\t\t\tif let Some(dash_offset) = dash_offset {\n\t\t\t\tif dash_offset != 0. {\n\t\t\t\t\tself.render_context.set_line_dash_offset(dash_offset);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tself.render_context\n\t\t\t\t.set_line_dash(&JsValue::from(array))\n\t\t\t\t.map_err(|error| log::warn!(\"Error drawing dashed line: {:?}\", error))\n\t\t\t\t.ok();","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/overlays/utility_types_web.rs#L391-L427","documentation":"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.","triggerScenarios":"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.","commonSituations":"Zoom or resize edge cases producing 0/NaN scale; gizmo overlays receiving document transforms that became singular after extreme scaling values.","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."],"exampleFix":"// before\nself.render_context.transform(a, b, c, d, e, f).expect(\"Failed to transform circle\");\n\n// after\nlet invertible = [a, b, c, d, e, f].iter().all(|v| v.is_finite()) && a * d - b * c != 0.;\nif invertible {\n\tif let Err(err) = self.render_context.transform(a, b, c, d, e, f) {\n\t\tlog::warn!(\"Failed to apply circle transform: {:?}\", err);\n\t}\n}","handlingStrategy":"validation","validationCode":"fn affine_is_drawable(transform: DAffine2) -> bool {\n\tlet [a, b, c, d, e, f] = transform.to_cols_array();\n\t[a, b, c, d, e, f].iter().all(|v| v.is_finite()) && a * d - b * c != 0.\n}","typeGuard":"fn is_invertible_2d(transform: DAffine2) -> bool {\n\tlet det = transform.matrix2.determinant();\n\tdet.is_finite() && det != 0.\n}","tryCatchPattern":"if let Err(err) = self.render_context.transform(a, b, c, d, e, f) {\n\tlog::warn!(\"failed to apply canvas transform: {:?}\", err);\n}","preventionTips":["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"],"tags":["wasm","canvas-2d","overlay","matrix","nan","panic"],"backgroundTag":"canvas-2d-transform-non-invertible","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}