a-b-street/abstreet · error

Failed to load svg from bytes. cache_key

Error message

Failed to load svg from bytes. cache_key: {}

What it means

widgetry's Image widget loads SVG images from a source; when the source is raw bytes, `load` calls `svg::load_svg_bytes` and panics if parsing fails, including the cache_key to identify which image broke. The library treats an unloadable SVG as a programming error rather than a recoverable failure.

Solutions

  1. Verify the bytes are well-formed SVG (e.g. parse with an XML/SVG parser) before constructing ImageSource::Bytes
  2. Ensure the source file is actually SVG (starts with <svg or <?xml), not PNG/JPEG
  3. Check that the file was fully read/truncated correctly (compare byte length)
  4. Use ImageSource::Path instead of Bytes so the path appears in the failure and existing load path is used
  5. Log the cache_key and first bytes of the input to identify the bad source

Example fix

// before
Image::from_bytes(cache_key, std::fs::read(path)?)
// after
let bytes = std::fs::read(path)?;
assert!(String::from_utf8_lossy(&bytes).contains("<svg"), "{} is not SVG", path.display());
Image::from_bytes(cache_key, bytes)
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_svg(bytes: &[u8]) -> bool {
    let s = String::from_utf8_lossy(bytes);
    s.contains("<svg") && !bytes.is_empty()
}

Prevention

When it happens

Trigger: Calling `ImageSource::Bytes { bytes, cache_key }` with bytes that are not valid SVG (empty bytes, PNG/JPEG data, truncated or malformed XML) and then calling `Image::load`/`Widget::draw` during prerender.

Common situations: Embedding an image downloaded at runtime that turned out to be a raster format; a build script or template produced empty/malformed SVG bytes; file extension and actual content disagree.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/17ab3d664991cf1a. Report an issue: GitHub.

Appendix: source

Thrown at widgetry/src/widgets/image.rs:46

    /// UTF-8 encoded bytes of an SVG
    Bytes { bytes: &'a [u8], cache_key: &'a str },

    /// Previously rendered graphics, in the form of a [`GeomBatch`], can
    /// be packaged as an `Image`.
    GeomBatch(GeomBatch, geom::Bounds),
}

impl ImageSource<'_> {
    /// Process `self` into a [`GeomBatch`].
    ///
    /// The underlying implementation makes use of caching to avoid re-parsing SVGs.
    pub fn load(&self, prerender: &crate::Prerender) -> (GeomBatch, geom::Bounds) {
        use crate::svg;
        match self {
            ImageSource::Path(image_path) => svg::load_svg(prerender, image_path),
            ImageSource::Bytes { bytes, cache_key } => {
                svg::load_svg_bytes(prerender, cache_key, bytes).unwrap_or_else(|_| {
                    panic!("Failed to load svg from bytes. cache_key: {}", cache_key)
                })
            }
            ImageSource::GeomBatch(geom_batch, bounds) => (geom_batch.clone(), *bounds),
        }
    }
}

impl<'a, 'c> Image<'a, 'c> {
    /// An `Image` with no renderable content. Useful for starting a template for creating
    /// several similar images using a builder pattern.
    pub fn empty() -> Self {
        Self {
            ..Default::default()
        }
    }

    /// Create an SVG `Image`, read from `filename`, which is colored to match `Style.icon_fg`
    pub fn from_path(filename: &'a str) -> Self {

View on GitHub (pinned to 0964f29315)