zed-industries/zed · error

invalid Canvas font size

Error message

invalid Canvas font size

What it means

CanvasFont::css_font builds the CSS font shorthand string (e.g. "italic 700 16px family") that is passed to the browser's canvas text APIs. Canvas requires a finite, positive font size; if the incoming Pixels value is NaN, infinite, zero, or negative, the method throws this error instead of producing an invalid CSS font string that the browser would silently reject.

Solutions

  1. Clamp font_size at the call site: if !size.is_finite() || size <= 0.0, substitute a sane default before invoking text measurement
  2. Trace where the NaN/zero/negative size originates (often a division by zero scale or an unmeasured parent)
  3. Give text styles a positive default font size in config/theme instead of 0.0
  4. Defer measurement until layout has produced real dimensions (e.g. skip measuring while viewport size is 0)

Example fix

// before
let font_size = style.font_size; // may be 0.0 or NaN
let metrics = text_system.layout_line(...)?;
// after
let font_size = if style.font_size.is_finite() && style.font_size > px(0.0) {
    style.font_size
} else {
    px(16.0) // sane default instead of invalid Canvas font size
};
let metrics = text_system.layout_line(...)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_canvas_font_size(size: Pixels) -> bool {
    size.0.is_finite() && size.0 > 0.0
}
// guard before any text measurement call
if !is_valid_canvas_font_size(style.font_size) { style.font_size = px(16.0); }

Type guard

fn valid_font_size(size: Pixels) -> Option<Pixels> {
    (size.0.is_finite() && size.0 > 0.0).then_some(size)
}

Try / catch

match text_system.layout_line(line, font_size, ...) {
    Ok(result) => result,
    Err(err) if err.to_string().contains("invalid Canvas font size") => {
        // retry with a safe default size
        text_system.layout_line(line, px(16.0), ...)?
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Calling css_font (via measure, typographic_bounds, advance, or glyph rasterization paths on a Canvas font) with a font_size that is non-finite or <= 0 — e.g. NaN/inf from a failed layout computation, a zero size from an empty window/viewport, or negative sizes from sign errors in scaling code.

Common situations: A text style whose font_size was divided by a zero scale factor (NaN), a zoom/animation tween passing through 0, a viewport measurement of 0 before layout settles, or a misconfigured theme providing a non-positive default size.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of zed-industries/zed@916fc2b8cb (2026-09-19). Data as JSON: /api/errors/3848c8abd5843575. Report an issue: GitHub.

Appendix: source

Thrown at crates/gpui_web/src/text_system.rs:449

    }

    fn recommended_rendering_mode(&self, font_id: FontId, font_size: Pixels) -> TextRenderingMode {
        if self.canvas_font(font_id).is_some() {
            TextRenderingMode::Grayscale
        } else {
            self.native.recommended_rendering_mode(font_id, font_size)
        }
    }

    fn glyph_dilation_for_color(&self, color: Hsla) -> u8 {
        self.native.glyph_dilation_for_color(color)
    }
}

impl CanvasFont {
    fn css_font(&self, font_size: Pixels, color: bool) -> Result<String> {
        let font_size = f32::from(font_size);
        ensure!(
            font_size.is_finite() && font_size > 0.,
            "invalid Canvas font size"
        );
        let style = match self.descriptor.style {
            FontStyle::Normal => "normal",
            FontStyle::Italic => "italic",
            FontStyle::Oblique => "oblique",
        };
        let mut families = Vec::new();
        if !color && !self.descriptor.family.starts_with('.') {
            families.push(quote_family(&self.descriptor.family));
        }
        if let Some(fallbacks) = &self.descriptor.fallbacks {
            families.extend(
                fallbacks
                    .fallback_list()
                    .iter()
                    .map(|family| quote_family(family)),

View on GitHub (pinned to 916fc2b8cb)