denoland/deno · error
icon ' ' must be .icns or .png
Error message
icon '{}' must be .icns or .png What it means
Thrown by resolve_hmr_icon_path (macOS only) when the single icon file exists but its extension is neither .icns nor .png — the only two formats the LAUFEY backend accepts via LAUFEY_APP_ICON / `-[NSImage initWithContentsOfFile:]`. The check is on the file extension, not on decoded image content.
Solutions
- Convert the image to PNG (`magick input.svg output.png`) or build a proper .icns with iconutil/Icon Composer.
- If the file IS a png/icns but misnamed, rename it to the exact lowercase extension: `mv AppIcon.PNG AppIcon.png`.
- Use a real conversion — merely changing the extension of a JPG/ICO does not change the file format and will fail at render time.
Example fix
# before deno desktop --hmr --icon assets/AppIcon.svg main.ts # after magick assets/AppIcon.svg assets/AppIcon.png deno desktop --hmr --icon assets/AppIcon.png main.ts
Defensive patterns
Strategy: validation
Validate before calling
# bash: enforce the accepted extensions ICON="$1" case "$ICON" in *.icns|*.png) ;; *) echo "icon must be .icns or .png" >&2; exit 1;; esac deno desktop --hmr --icon "$ICON" main.ts
Type guard
// TypeScript
function isAcceptedHmrIcon(p: string): boolean {
return p.endsWith(".icns") || p.endsWith(".png");
} Prevention
- Convert design sources (SVG/JPG) to PNG or build an .icns in your asset pipeline.
- Use lowercase extensions (.png, .icns) — the check is exact.
- Remember renaming a file's extension does not convert its format.
When it happens
Trigger: `--icon assets/AppIcon.svg`, `.jpg`, `.jpeg`, `.ico`, `.webp`, or `.bmp` on macOS with `deno desktop --hmr`; a file with no extension; upper-case extensions like `.PNG` are also rejected because the match is on the exact lowercase strings "icns"/"png".
Common situations: Designers hand over SVG or JPG source assets; a .ico reused from a Windows build; file named `AppIcon.icns.txt` after a bad download; uppercase extension from a case-renaming script.
Related errors
- icon ' ' not found
- icon sets are not supported in --hmr mode yet
- bundle identifier is longer than 155 characters
- app bundle has no name
- bundle identifier has an empty segment
AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20).
Data as JSON: /api/errors/6a88fa802e9efbbf.
Report an issue: GitHub.
Appendix: source
Thrown at cli/tools/desktop.rs:1173
/// laufey passes to `-[NSImage initWithContentsOfFile:]` (both formats are
/// accepted, so no conversion is needed).
#[cfg(target_os = "macos")]
fn resolve_hmr_icon_path(
icon: &crate::args::IconConfig,
initial_cwd: &Path,
) -> Result<PathBuf, AnyError> {
let icon_path = match icon {
crate::args::IconConfig::Single(p) => initial_cwd.join(p),
crate::args::IconConfig::Set(_) => {
deno_core::anyhow::bail!("icon sets are not supported in --hmr mode yet")
}
};
if !icon_path.exists() {
deno_core::anyhow::bail!("icon '{}' not found", icon_path.display());
}
match icon_path.extension().and_then(|e| e.to_str()) {
Some("icns") | Some("png") => {}
_ => deno_core::anyhow::bail!(
"icon '{}' must be .icns or .png",
icon_path.display()
),
}
Ok(crate::util::fs::canonicalize_path(&icon_path).unwrap_or(icon_path))
}
/// Extract the local URL from a line of dev server output.
fn parse_dev_server_url(line: &str) -> Option<String> {
let line = console_static_text::ansi::strip_ansi_codes(line);
// Vite prints ` ➜ Local: http://localhost:5173/`
regex::Regex::new(r"Local:\s+(https?://\S+)")
.expect("regex to parse local url failed")
.captures(&line)
.and_then(|c| c.get(1))
.map(|m| m.as_str().to_owned())
}
View on GitHub (pinned to f7822238ca)