plandex-ai/plandex · error

failed to create gzip reader: %w

Error message

failed to create gzip reader: %w

What it means

doUpgrade wraps the downloaded archive in a gzip.NewReader to decompress the tar.gz release. If the stream's first bytes are not valid gzip data, it returns 'failed to create gzip reader: %w'. This almost always means the downloaded file is not the expected release tarball.

Source

Thrown at app/cli/upgrade.go:131

		return fmt.Errorf("failed to create temporary file: %w", err)
	}
	defer os.Remove(tempFile.Name()) // Clean up file afterwards

	// Copy the response body to the temporary file
	_, err = io.Copy(tempFile, resp.Body)
	if err != nil {
		return fmt.Errorf("failed to save the downloaded archive: %w", err)
	}

	_, err = tempFile.Seek(0, 0)
	if err != nil {
		return fmt.Errorf("failed to seek in temporary file: %w", err)
	}

	// Now, extract the binary from the tempFile
	gzr, err := gzip.NewReader(tempFile)
	if err != nil {
		return fmt.Errorf("failed to create gzip reader: %w", err)
	}
	defer gzr.Close()

	tarReader := tar.NewReader(gzr)
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break // End of archive
		}
		if err != nil {
			return fmt.Errorf("failed to read tar header: %w", err)
		}

		// Check if the current file is the binary
		if header.Typeflag == tar.TypeReg && (header.Name == "plandex" || header.Name == "plandex.exe") {
			err = update.Apply(tarReader, update.Options{})
			if err != nil {
				if errors.Is(err, fs.ErrPermission) {

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check resp.StatusCode == 200 before saving the body (404/HTML pages cause this).
  2. Verify the release tag cli/v<version> and asset plandex_<version>_<GOOS>_<GOARCH>.tar.gz exist on GitHub.
  3. Clear/adjust proxy settings that may intercept the download.
  4. Retry the download to rule out truncation.

Example fix

// before (no status check; error page saved as tar.gz)
resp, err := http.Get(downloadURL)
if err != nil {
	return fmt.Errorf("failed to download the update: %w", err)
}
defer resp.Body.Close()
// after
if resp.StatusCode != http.StatusOK {
	return fmt.Errorf("unexpected download status: %s", resp.Status)
}
Defensive patterns

Strategy: validation

Validate before calling

// before decompressing, check HTTP status and magic bytes
if resp.StatusCode != http.StatusOK {
	return fmt.Errorf("unexpected status: %s", resp.Status)
}
head := make([]byte, 2)
if _, err := io.ReadFull(tempFile, head); err != nil {
	return fmt.Errorf("archive too short: %w", err)
}
if head[0] != 0x1f || head[1] != 0x8b {
	return fmt.Errorf("not a gzip file")
}
tempFile.Seek(0, io.SeekStart)

Try / catch

gzr, err := gzip.NewReader(tempFile)
if err != nil {
	return fmt.Errorf("failed to create gzip reader: %w — downloaded file may not be a valid release archive", err)
}

Prevention

When it happens

Trigger: gzip.NewReader(tempFile) fails because the downloaded content is not gzip — a 404/HTML error page was saved instead of the archive, the download was truncated, or a proxy returned an intercepted response.

Common situations: Release tag typo/version not published so GitHub returns a 404 page; corporate proxy injecting an HTML block page; partial download from a dropped connection; wrong GOOS/GOARCH asset name.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/cdead845249efb71. Report an issue: GitHub.