{"record":{"id":"f501d6d315226fd0","repo":"RightNow-AI/openfang","slug":"failed-to-decode-tray-icon-png","errorCode":null,"errorMessage":"Failed to decode tray icon PNG","messagePattern":"Failed to decode tray icon PNG","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/openfang-desktop/src/tray.rs","lineNumber":109,"sourceCode":"        app,\n        &[\n            &show,\n            &browser,\n            &sep1,\n            &agents_info,\n            &status_info,\n            &sep2,\n            &launch_at_login,\n            &check_updates,\n            &open_config,\n            &sep3,\n            &quit,\n        ],\n    )?;\n\n    // Load the tray icon from embedded PNG bytes\n    let tray_icon = tauri::image::Image::from_bytes(include_bytes!(\"../icons/32x32.png\"))\n        .expect(\"Failed to decode tray icon PNG\");\n\n    let _tray = TrayIconBuilder::new()\n        .icon(tray_icon)\n        .menu(&menu)\n        .tooltip(\"OpenFang Agent OS\")\n        .on_menu_event(move |app, event| match event.id().as_ref() {\n            \"show\" => {\n                if let Some(w) = app.get_webview_window(\"main\") {\n                    let _ = w.show();\n                    let _ = w.unminimize();\n                    let _ = w.set_focus();\n                }\n            }\n            \"browser\" => {\n                if let Some(port) = app.try_state::<crate::PortState>() {\n                    let url = format!(\"http://127.0.0.1:{}\", port.0);\n                    let _ = open::that(&url);\n                }","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/RightNow-AI/openfang/blob/acf2587e46be174c10200489c9a2d23a39a98aeb/crates/openfang-desktop/src/tray.rs#L91-L127","documentation":"This panic comes from `tauri::image::Image::from_bytes(include_bytes!(\"../icons/32x32.png\")).expect(...)` in setup_tray (crates/openfang-desktop/src/tray.rs:109). The app embeds its tray icon PNG at compile time and decodes it at runtime; Tauri's Image::from_bytes expects raw decoded RGBA data or a recognized format and returns an Err when the byte content cannot be decoded into an Image (it does not auto-decode PNG unless the 'image-png' feature is enabled).","triggerScenarios":"(1) The tauri 'image-png' (or image-ico) cargo feature is missing, so from_bytes receives compressed PNG bytes it cannot interpret; (2) the embedded ../icons/32x32.png file is corrupt, empty, truncated, or not actually a PNG; (3) the file path in include_bytes! points to a wrong/renamed asset so unrelated bytes are embedded.","commonSituations":"Upgrading Tauri v1 to v2 and forgetting the image-png feature; replacing icons/icon.png with a mislabeled file (e.g. an SVG or ICO renamed .png); a bad git merge or LFS checkout leaving a pointer file instead of real PNG bytes.","solutions":["Enable the tauri 'image-png' feature in Cargo.toml: tauri = { version = \"2\", features = [\"image-png\", \"tray-icon\"] }.","Verify ../icons/32x32.png is a valid PNG (file icons/32x32.png; ensure it is not a Git LFS pointer or truncated file).","Alternatively use Image::from_path at build/first-run, or decode with the image crate and pass raw RGBA to Image::new_owned.","Replace expect with graceful error handling so a bad asset logs an error instead of aborting tray setup."],"exampleFix":"// before\nlet tray_icon = tauri::image::Image::from_bytes(include_bytes!(\"../icons/32x32.png\"))\n    .expect(\"Failed to decode tray icon PNG\");\n// after (Cargo.toml: tauri features += [\"image-png\"])\nlet tray_icon = tauri::image::Image::from_bytes(include_bytes!(\"../icons/32x32.png\"))\n    .unwrap_or_else(|e| {\n        log::error!(\"tray icon decode failed: {e}\");\n        tauri::image::Image::new_rgba(Vec::new(), 32, 32).expect(\"fallback icon\")\n    });","handlingStrategy":"fallback","validationCode":"// Compile-time sanity: ensure the embedded asset exists and is a PNG\nconst _: () = assert!(include_bytes!(\"../icons/32x32.png\")[..8] == [0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A]);","typeGuard":"fn is_png(bytes: &[u8]) -> bool {\n    bytes.len() >= 8 && bytes[..8] == [0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A]\n}","tryCatchPattern":"let tray_icon = tauri::image::Image::from_bytes(include_bytes!(\"../icons/32x32.png\"));\nlet tray_icon = match tray_icon {\n    Ok(img) => img,\n    Err(e) => {\n        log::error!(\"tray icon decode failed: {e}\");\n        return Ok(()); // skip tray setup instead of crashing app startup\n    }\n};","preventionTips":["Enable the tauri 'image-png' feature when embedding PNGs for tray/window icons.","Add a unit test that decodes include_bytes!(\"../icons/32x32.png\") in CI to catch corrupt/renamed assets.","Verify icons after git LFS checkouts and merges (LFS pointer files look like text, not PNGs).","Provide a hard-coded fallback icon so a bad asset degrades instead of aborting startup."],"tags":["rust","tauri","tray-icon","image-decoding","desktop"],"backgroundTag":"image-decode-failed","analyzedSha":"acf2587e46be174c10200489c9a2d23a39a98aeb","analyzedAt":"2026-09-02T22:42:28.464Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}