JanDeDobbeleer/oh-my-posh · error
%s is not a valid zip file
Error message
%s is not a valid zip file
What it means
oh-my-posh's font downloader (`oh-my-posh font download <name>`) fetches a font archive from a remote URL and validates it is a ZIP before extracting. This error means the bytes downloaded from `fontURL` failed the ZIP magic-number check (`isZipFile`), so the installer refuses to process the payload. It guards against HTML error pages, redirects to login pages, or corrupted transfers being treated as font archives.
Source
Thrown at src/cli/font/download.go:40
if zipPath, OK := cache.Device.Get[string](fontURL); OK {
if b, err := os.ReadFile(zipPath); err == nil {
return b, nil
}
}
// validate if we have a local file
u, err := url.Parse(fontURL)
if err != nil || u.Scheme != "https" {
return nil, errors.New("font path must be a valid URL")
}
var b []byte
if b, err = getRemoteFile(fontURL, report); err != nil {
return nil, err
}
if !isZipFile(b) {
return nil, fmt.Errorf("%s is not a valid zip file", fontURL)
}
fileName := path.Base(fontURL)
zipPath := filepath.Join(os.TempDir(), fileName)
tempFile, err := os.Create(zipPath)
defer func() {
_ = tempFile.Close()
}()
if err != nil {
return b, nil
}
_, err = tempFile.Write(b)
if err != nil {
return b, nil
}View on GitHub (pinned to 0976794618)
Solutions
- Verify the URL returns a real .zip: `curl -sIL <url>` and check Content-Type (application/zip) and size
- Re-run the command — a transient proxy/cache issue may have served a bad body
- Use an official release asset URL (e.g. a Nerd Fonts release zip) rather than a repo page URL
- Check for corporate proxy interference (set HTTP(S)_PROXY correctly or bypass it)
- If persistent, report the URL at github.com/JanDeDobbeleer/oh-my-posh/issues
Example fix
// before: URL to an HTML release page oh-my-posh font download "https://github.com/ryanoasis/nerd-fonts/releases" // after: URL to the actual zip asset oh-my-posh font download "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/FiraCode.zip"
Defensive patterns
Strategy: validation
Validate before calling
func isZip(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
magic := make([]byte, 2)
if _, err := io.ReadFull(f, magic); err != nil {
return err
}
if magic[0] != 'P' || magic[1] != 'K' {
return fmt.Errorf("%s is not a zip file", path)
}
return nil
}
// run isZip() on the URL's payload (or curl -sI and check Content-Type: application/zip) before installing Try / catch
if err := fontDownload(url); err != nil {
if strings.Contains(err.Error(), "is not a valid zip file") {
// inspect URL / retry with the canonical release asset URL
}
} Prevention
- Only pass direct URLs to .zip release assets, never HTML release pages
- Check Content-Type is application/zip before trusting a 200 response
- Beware proxies/CDNs that return 200 with an HTML block page
When it happens
Trigger: `download(fontURL, report)` calls `getRemoteFile` successfully (HTTP 200) but the response body does not start with the ZIP signature (PK). Typical when the URL points to an HTML page instead of the archive, a CDN/proxy returns an error or captcha page with status 200, or the file is a .tar.gz instead of a .zip.
Common situations: GitHub codeload URL changed or redirected to an HTML page; corporate proxy/SSL-inspection appliance substituting a block page; manually specified URL for a release asset that is actually a tarball; truncated download served with 200 OK.
Related errors
- failed to download zip file: %s → %s
- no assets found
- failed to parse nerd fonts release
- the wasm build renders from data only and cannot make reques
- not connected
AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31).
Data as JSON: /api/errors/bccafc3b808fa04a.
Report an issue: GitHub.