a-b-street/abstreet · error

error loading svg

Error message

error loading svg: {}

What it means

load_svg panics when parsing the SVG bytes (via usvg) fails after reading the asset. The bytes were fetched through the read_svg hook, but usvg::Tree parsing or conversion produced an error, so load_svg_from_bytes_uncached returns Err and load_svg unwraps it into a panic naming the file.

Solutions

  1. Open the named file and validate it is well-formed SVG XML.
  2. Check the custom read_svg implementation isn't returning empty bytes on failure.
  3. Re-export/clean the SVG asset (e.g. resave with Inkscape/plain SVG profile).
  4. Use load_svg_bytes for tolerant handling if you control the call path.

Example fix

// before
let bytes = std::fs::read("assets/map.svg").unwrap();
// after
assert!(std::fs::read("assets/map.svg")?.starts_with(b"<"), "map.svg is not SVG");
Defensive patterns

Strategy: validation

Validate before calling

// Check the asset parses before calling load_svg
let bytes = std::fs::read(path)?;
assert!(!bytes.is_empty(), "{} is empty", path);
usvg::Tree::from_str(&String::from_utf8_lossy(&bytes), &usvg::Options::default()).expect("invalid svg");

Try / catch

match load_svg_bytes(prerender, key, &bytes) { Ok(b) => b, Err(_) => default_batch }

Prevention

When it happens

Trigger: Calling load_svg with a file whose contents are invalid/unparsable SVG: empty file, HTML error page saved as .svg, malformed XML, SVG features usvg rejects, or read_svg returning garbage/empty bytes on failure.

Common situations: Custom read_svg implementations that swallow open errors and return empty Vec (leading to parse failure here), corrupted assets in the repo, downloading assets over a proxy returning HTML.

Related errors


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

Appendix: source

Thrown at widgetry/src/svg.rs:30

pub const HIGH_QUALITY: f32 = 0.01;
pub const LOW_QUALITY: f32 = 1.0;

// Code here adapted from
// https://github.com/nical/lyon/blob/0d0ee771180fb317b986d9cf30266722e0773e01/examples/wgpu_svg/src/main.rs

pub fn load_svg(prerender: &Prerender, filename: &str) -> (GeomBatch, Bounds) {
    let cache_key = format!("file://{}", filename);
    if let Some(pair) = prerender.assets.get_cached_svg(&cache_key) {
        return pair;
    }

    let bytes = (prerender.assets.read_svg)(filename);
    load_svg_from_bytes_uncached(&bytes)
        .map(|(batch, bounds)| {
            prerender.assets.cache_svg(cache_key, batch.clone(), bounds);
            (batch, bounds)
        })
        .unwrap_or_else(|_| panic!("error loading svg: {}", filename))
}

pub fn load_svg_bytes(
    prerender: &Prerender,
    cache_key: &str,
    bytes: &[u8],
) -> anyhow::Result<(GeomBatch, Bounds)> {
    let cache_key = format!("bytes://{}", cache_key);
    if let Some(pair) = prerender.assets.get_cached_svg(&cache_key) {
        return Ok(pair);
    }

    load_svg_from_bytes_uncached(bytes).map(|(batch, bounds)| {
        prerender.assets.cache_svg(cache_key, batch.clone(), bounds);
        (batch, bounds)
    })
}

View on GitHub (pinned to 0964f29315)