tauri-apps/tauri · error

failed to decode icon

Error message

failed to decode icon {}: {}

What it means

After the ICO container parses, new_ico decodes its first entry's image into RGBA. If entry.decode() fails — unsupported bit depth, compression variant, or corrupt image data inside a structurally valid ICO — this panic fires with the icon path and decode error.

Solutions

  1. Re-encode the .ico with standard BMP-embedded 32-bit RGBA frames (e.g. via ImageMagick or icoconvert)
  2. Remove or reorder entries so the first entry is a standard size like 32x32
  3. Open the icon in an image editor and re-export it
  4. Fall back to a plain PNG icon via the PNG code path

Example fix

// before
ico made by exotic tool, png-compressed frames
// after
magick icon.png -define icon:auto-resize=32,16 -compress zip icon.ico
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image
im = Image.open('icon.ico')
im.load()  # raises if frames are corrupt/undecodable
print(im.size, im.mode)

Prevention

When it happens

Trigger: An .ico whose first entry uses an unsupported encoding (e.g. exotic compression or PNG-compressed entry the `ico` crate cannot decode); truncated image data inside the ICO.

Common situations: Icons exported by niche tools with unusual internal formats; ICOs containing PNG-compressed frames; corrupted assets after a bad merge or LFS checkout.

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 tauri-apps/tauri@460ec35447 (2026-09-18). Data as JSON: /api/errors/05726f4c09634c40. Report an issue: GitHub.

Appendix: source

Thrown at crates/tauri-codegen/src/image.rs:60

    let buf = Self::open(icon);
    Cached::try_from(buf).map(|cache| Self {
      cache,
      root: root.clone(),
      format: IconFormat::Raw,
    })
  }

  /// Cache an ICO icon as RGBA data, see [`ImageFormat::Image`].
  pub fn new_ico(root: &TokenStream, icon: &Path) -> EmbeddedAssetsResult<Self> {
    let buf = Self::open(icon);

    let icon_dir = ico::IconDir::read(Cursor::new(&buf))
      .unwrap_or_else(|e| panic!("failed to parse icon {}: {}", icon.display(), e));

    let entry = &icon_dir.entries()[0];
    let rgba = entry
      .decode()
      .unwrap_or_else(|e| panic!("failed to decode icon {}: {}", icon.display(), e))
      .rgba_data()
      .to_vec();

    Cached::try_from(rgba).map(|cache| Self {
      cache,
      root: root.clone(),
      format: IconFormat::Image {
        width: entry.width(),
        height: entry.height(),
      },
    })
  }

  /// Cache a PNG icon as RGBA data, see [`ImageFormat::Image`].
  pub fn new_png(root: &TokenStream, icon: &Path) -> EmbeddedAssetsResult<Self> {
    let buf = Self::open(icon);
    let decoder = png::Decoder::new(Cursor::new(&buf));
    let mut reader = decoder

View on GitHub (pinned to 460ec35447)