zed-industries/zed · error

Canvas text raster returned

Error message

Canvas text raster returned {} bytes, expected {byte_count}

What it means

After drawing text, `rasterize` reads back the canvas with get_image_data and verifies the returned pixel buffer length equals the expected byte_count (width * height * 4 bytes RGBA). This error is thrown when the browser returned a different number of bytes than requested. Since the canvas is resized to exactly width x height before drawing, a mismatch indicates a browser/implementation inconsistency rather than caller error.

Solutions

  1. Verify the browser's own canvas size limits (check canvas.width/canvas.height after resize); reduce the raster size below the browser's cap.
  2. Update the browser/WebView runtime; older OffscreenCanvas implementations have getImageData bugs.
  3. Retry the rasterize with smaller bounds if the runtime silently clamped the canvas.
  4. If persistent, log actual vs expected sizes and report the runtime; work around by tiling the raster.

Example fix

// before
let image = ctx.get_image_data(0.0, 0.0, width as f64, height as f64)?;
// after (caller-side guard)
let width = width.min(max_supported_canvas_dimension(&runtime));
let height = height.min(max_supported_canvas_dimension(&runtime));
rasterize(text, font, bounds_with_size(width, height), offset, color)?;
Defensive patterns

Strategy: try-catch

Validate before calling

let pixels = (width as usize) * (height as usize) * 4;
// No caller-side pre-check can fully prevent this; the mismatch is browser-side.

Try / catch

match rasterize(text, font, bounds, offset, color) {
    Ok(pixels) => upload_to_atlas(pixels),
    Err(err) if err.to_string().contains("returned") && err.to_string().contains("bytes") => {
        log::warn!("canvas readback size mismatch: {err}; retrying with smaller bounds");
        retry_with_tiled_bounds(text, font, bounds, offset, color)
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: The OffscreenCanvas 2D backend returns an ImageData whose size does not match the get_image_data request — e.g. the canvas resize (set_width/set_height) silently failed or was clamped by the browser (browsers cap canvas size, returning a smaller buffer), or a buggy WebView implementation of OffscreenCanvas getImageData.

Common situations: Browsers or embedded WebViews that silently clamp canvas dimensions below the requested size (some cap total canvas area or max dimension independently); memory-constrained environments where the resize failed; non-conformant OffscreenCanvas implementations in older runtimes.

Related errors


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

Appendix: source

Thrown at crates/gpui_web/src/canvas_text.rs:178

        }
        canvas.configure(css_font)?;
        canvas
            .context
            .clear_rect(0.0, 0.0, f64::from(width), f64::from(height));
        canvas
            .context
            .fill_text(
                text,
                -f64::from(bounds.origin.x.0) + f64::from(subpixel_offset.0),
                -f64::from(bounds.origin.y.0) + f64::from(subpixel_offset.1),
            )
            .map_err(|error| anyhow!("drawing Canvas text: {error:?}"))?;
        let image = canvas
            .context
            .get_image_data(0.0, 0.0, f64::from(width), f64::from(height))
            .map_err(|error| anyhow!("reading Canvas text raster pixels: {error:?}"))?;
        let mut pixels = image.data().0;
        ensure!(
            pixels.len() == byte_count,
            "Canvas text raster returned {} bytes, expected {byte_count}",
            pixels.len()
        );
        if color {
            for pixel in pixels.chunks_exact_mut(4) {
                pixel.swap(0, 2);
            }
            Ok(pixels)
        } else {
            let mut alpha = Vec::new();
            alpha
                .try_reserve_exact(pixel_count)
                .context("allocating Canvas text alpha mask")?;
            alpha.extend(pixels.chunks_exact(4).map(|pixel| pixel[3]));
            Ok(alpha)
        }
    })

View on GitHub (pinned to 916fc2b8cb)