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

  1. Verify the URL returns a real .zip: `curl -sIL <url>` and check Content-Type (application/zip) and size
  2. Re-run the command — a transient proxy/cache issue may have served a bad body
  3. Use an official release asset URL (e.g. a Nerd Fonts release zip) rather than a repo page URL
  4. Check for corporate proxy interference (set HTTP(S)_PROXY correctly or bypass it)
  5. 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

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


AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31). Data as JSON: /api/errors/bccafc3b808fa04a. Report an issue: GitHub.