GraphiteEditor/Graphite · warning

transform should be able to be reset to be able to account…

Error message

transform should be able to be reset to be able to account for DPI

What it means

end_dpi_aware_transform calls ctx.reset_transform(). Per the HTML spec resetTransform() is equivalent to setTransform(1, 0, 0, 1, 0, 0), and the identity matrix is always invertible, so on a live 2D context this call effectively cannot throw; web_sys still types it as Result and the .expect is purely defensive. If it ever fires it indicates an invalid or lost context rather than a geometry bug.

Solutions

  1. Treat it as defensive: if observed, check canvas/context health and look for an earlier root-cause panic (e.g., a set_transform NaN failure in the same frame).
  2. Degrade to a logged .ok() so a context anomaly cannot abort the overlay pass.
  3. Ensure the context cache is invalidated when the underlying canvas element is recreated.

Example fix

// before
self.render_context.reset_transform().expect("transform should be able to be reset to be able to account for DPI");

// after — resetTransform() applies the identity matrix and cannot fail on a live context
if let Err(err) = self.render_context.reset_transform() {
	log::warn!("Failed to reset canvas transform: {:?}", err);
}
Defensive patterns

Strategy: fallback

Try / catch

if let Err(err) = self.render_context.reset_transform() {
	log::warn!("failed to reset canvas transform (context may be invalid): {:?}", err);
}

Prevention

When it happens

Trigger: Practically unreachable on a valid context. Conceivable only with an already-invalid context (lost after a GPU reset or detached canvas) or an engine-level failure inside resetTransform.

Common situations: Appearing in crash reports almost always as a secondary effect of a corrupted render context or a stale cached context after the canvas element was replaced — not as an independent defect.

Related errors


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

Appendix: source

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

			self.draw_triangle(mid + edge * (3. + SKEW_TRIANGLE_OFFSET), edge, SKEW_TRIANGLE_SIZE, None, None);
		}
	}

	/// Transforms the canvas context to adjust for DPI scaling
	///
	/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].
	fn start_dpi_aware_transform(&self) {
		let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.viewport.scale())).to_cols_array();
		self.render_context
			.set_transform(a, b, c, d, e, f)
			.expect("transform should be able to be set to be able to account for DPI");
	}

	/// Un-transforms the Canvas context to adjust for DPI scaling
	///
	/// Warning: this function doesn't only reset the DPI scaling adjustment, it resets the entire transform.
	fn end_dpi_aware_transform(&self) {
		self.render_context.reset_transform().expect("transform should be able to be reset to be able to account for DPI");
	}

	pub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
		let size = size.unwrap_or(MANIPULATOR_GROUP_MARKER_SIZE);
		let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
		let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);

		let position = self.snap_to_physical_pixel_center(position);
		let corner = position - DVec2::splat(size) / 2.;

		self.start_dpi_aware_transform();

		self.render_context.begin_path();
		self.render_context.rect(corner.x, corner.y, size, size);
		self.render_context.set_fill_style_str(color_fill);
		self.render_context.set_stroke_style_str(color_stroke);
		self.render_context.set_line_width(1.);
		self.render_context.fill();

View on GitHub (pinned to c507b35645)