GraphiteEditor/Graphite · error

Failed to draw hover ring

Error message

Failed to draw hover ring

What it means

The compass/skew gizmo overlay draws its hover ring with ctx.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU). The radius is a positive constant, so the negative-radius IndexSizeError route is closed; the .expect can only fire from non-finite center coordinates (compass_center run through snap_to_physical_pixel_center becoming NaN) on engines that reject non-finite doubles, or a regression making the constant non-positive.

Source

Thrown at editor/src/messages/portfolio/document/overlays/utility_types_web.rs:742

		const HOVER_RING_STROKE_WIDTH: f64 = HOVER_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;
		const HOVER_RING_CENTERLINE_RADIUS: f64 = (HOVER_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.;
		const MAIN_RING_STROKE_WIDTH: f64 = MAIN_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;
		const MAIN_RING_CENTERLINE_RADIUS: f64 = (MAIN_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.;

		let Some(show_hover_ring) = show_compass_with_hover_ring else { return };

		self.start_dpi_aware_transform();

		let center = self.snap_to_physical_pixel_center(compass_center);

		// Save the old line width to restore it later
		let old_line_width = self.render_context.line_width();

		// Hover ring
		if show_hover_ring {
			self.render_context.set_line_width(HOVER_RING_STROKE_WIDTH);
			self.render_context.begin_path();
			self.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU).expect("Failed to draw hover ring");
			self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE_50);
			self.render_context.stroke();
		}

		// Arrows
		self.render_context.set_line_width(0.01);
		for i in 0..4 {
			let direction = DVec2::from_angle(i as f64 * FRAC_PI_2 + angle);
			let color = if i % 2 == 0 { COLOR_OVERLAY_RED } else { COLOR_OVERLAY_GREEN };

			let tip = center + direction * HOVER_RING_OUTER_RADIUS;
			let base = center + direction * (MAIN_RING_INNER_RADIUS + MAIN_RING_OUTER_RADIUS) / 2.;

			let r = (ARROW_RADIUS.powi(2) + MAIN_RING_INNER_RADIUS.powi(2)).sqrt();
			let (cos, sin) = (MAIN_RING_INNER_RADIUS / r, ARROW_RADIUS / r);
			let side1 = center + r * DVec2::new(cos * direction.x - sin * direction.y, sin * direction.x + direction.y * cos);
			let side2 = center + r * DVec2::new(cos * direction.x + sin * direction.y, -sin * direction.x + direction.y * cos);

View on GitHub (pinned to c507b35645)

Solutions

  1. Validate the snapped center is finite before drawing the ring.
  2. Trace NaN to its source in the compass center computation (document bounds, viewport transform).
  3. Degrade .expect to a logged .ok() so the gizmo fails soft.
  4. Keep gizmo radius constants positive via a const/debug assertion.

Example fix

// before
self.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU).expect("Failed to draw hover ring");

// after
if center.x.is_finite() && center.y.is_finite() {
	let _ = self.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU);
}
Defensive patterns

Strategy: validation

Validate before calling

fn compass_center_drawable(center: DVec2) -> bool {
	center.x.is_finite() && center.y.is_finite()
}

Type guard

fn is_finite_point(p: DVec2) -> bool {
	p.x.is_finite() && p.y.is_finite()
}

Try / catch

if let Err(err) = self
	.render_context
	.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU)
{
	log::warn!("failed to draw hover ring: {:?}", err);
}

Prevention

When it happens

Trigger: Rendering the transform compass hover ring when the computed compass center is NaN — typically from degenerate transform or viewport math upstream — or after a regression of the ring-radius constant.

Common situations: Gizmo overlays being the first consumer of corrupted geometry during pointer interaction; refactors turning gizmo constants into computed values.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/1593d3f5495a257c. Report an issue: GitHub.