{"record":{"id":"bea77a217980df88","repo":"GraphiteEditor/Graphite","slug":"failed-to-draw-ellipse","errorCode":null,"errorMessage":"Failed to draw ellipse","messagePattern":"Failed to draw ellipse","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/overlays/utility_types_web.rs","lineNumber":368,"sourceCode":"\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();\n\t\t}\n\n\t\tself.render_context.begin_path();\n\t\tself.render_context\n\t\t\t.ellipse_with_anticlockwise(\n\t\t\t\tcenter.x,\n\t\t\t\tcenter.y,\n\t\t\t\tradius_x,\n\t\t\t\tradius_y,\n\t\t\t\trotation.unwrap_or_default(),\n\t\t\t\tstart_angle.unwrap_or_default(),\n\t\t\t\tend_angle.unwrap_or(TAU),\n\t\t\t\tcounterclockwise.unwrap_or_default(),\n\t\t\t)\n\t\t\t.expect(\"Failed to draw ellipse\");\n\t\tself.render_context.set_stroke_style_str(color_stroke);\n\n\t\tif let Some(fill_color) = color_fill {\n\t\t\tself.render_context.set_fill_style_str(fill_color);\n\t\t\tself.render_context.fill();\n\t\t}\n\t\tself.render_context.stroke();\n\n\t\t// Reset the dash pattern back to solid\n\t\tif dash_width.is_some() {\n\t\t\tself.render_context\n\t\t\t\t.set_line_dash(&JsValue::from(js_sys::Array::new()))\n\t\t\t\t.map_err(|error| log::warn!(\"Error drawing dashed line: {:?}\", error))\n\t\t\t\t.ok();\n\t\t}\n\t\tif dash_offset.is_some() && dash_offset != Some(0.) {\n\t\t\tself.render_context.set_line_dash_offset(0.);\n\t\t}","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/overlays/utility_types_web.rs#L350-L386","documentation":"Calls ellipse_with_anticlockwise, which maps to JS CanvasRenderingContext2D.ellipse(). The browser throws IndexSizeError when radius_x or radius_y is negative, and errors can also occur with non-finite arguments; wasm-bindgen surfaces any JS throw as Err(JsValue), which this .expect escalates into a Rust panic that aborts the overlay render pass.","triggerScenarios":"Drawing an ellipse overlay whose radii were computed from degenerate geometry — mirrored or inverted bounds where the width/height difference goes negative, or NaN leaking from a corrupted document/viewport transform — so radius_x/radius_y arrive negative or non-finite.","commonSituations":"Flipped/mirrored layers producing negative values in bounding-box math; division by zero zoom yielding NaN upstream; fuzzed or malformed documents feeding non-finite geometry into overlay drawing.","solutions":["Clamp and validate radii before the call: early-return unless radius_x/radius_y are finite and >= 0.","Trace NaN to its source — add is_finite debug assertions on viewport/document transforms when they are computed, not when they are drawn.","Replace .expect with a logged .ok()/if-let so one bad ellipse cannot kill the whole overlay frame.","Log the ellipse arguments when the call fails so the offending geometry is identifiable in reports."],"exampleFix":"// before\nself.render_context\n\t.ellipse_with_anticlockwise(center.x, center.y, radius_x, radius_y, rotation, start_angle, end_angle, ccw)\n\t.expect(\"Failed to draw ellipse\");\n\n// after\nif radius_x.is_finite() && radius_y.is_finite() && radius_x >= 0. && radius_y >= 0. {\n\tif let Err(err) = self.render_context.ellipse_with_anticlockwise(center.x, center.y, radius_x, radius_y, rotation, start_angle, end_angle, ccw) {\n\t\tlog::warn!(\"Failed to draw ellipse: {:?}\", err);\n\t}\n}","handlingStrategy":"validation","validationCode":"fn ellipse_args_safe(center: DVec2, radius_x: f64, radius_y: f64) -> bool {\n\tcenter.x.is_finite()\n\t\t&& center.y.is_finite()\n\t\t&& radius_x.is_finite()\n\t\t&& radius_y.is_finite()\n\t\t&& radius_x >= 0.\n\t\t&& radius_y >= 0.\n}","typeGuard":"fn is_drawable_radius(radius: f64) -> bool {\n\tradius.is_finite() && radius >= 0.\n}","tryCatchPattern":"if let Err(err) = self\n\t.render_context\n\t.ellipse_with_anticlockwise(center.x, center.y, radius_x, radius_y, rot, start, end, ccw)\n{\n\tlog::warn!(\"overlay ellipse failed: {:?}\", err);\n}","preventionTips":["Clamp radii with .max(0.) wherever they are derived from bounds or zoom","Assert transforms and bounds are finite when computed, not when drawn","Log geometry values on draw failure so the NaN or negative source is identifiable"],"tags":["wasm","canvas-2d","overlay","geometry","nan","panic"],"backgroundTag":"canvas-2d-negative-radius","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}