multica-ai/multica · error
zip reader: %w
Error message
zip reader: %w
What it means
Returned by extractBinaryFromZip when archive/zip.NewReader rejects the buffered bytes as a valid zip archive. The update downloader fully buffers the release artifact in memory and then asks the zip package to parse its central directory; any corruption, truncation, or non-zip payload (e.g. an HTML error page from a proxy) surfaces here. The underlying error is usually zip.ErrFormat ('not a valid zip file') or a bad central-directory offset.
Source
Thrown at server/internal/cli/update.go:521
return nil, fmt.Errorf("read binary: %w", err)
}
return data, nil
}
}
}
// extractBinaryFromZip reads a .zip stream and returns the contents of the
// named file entry. The zip format requires random access, so the full archive
// is buffered in memory.
func extractBinaryFromZip(r io.Reader, name string) ([]byte, error) {
buf, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("read zip data: %w", err)
}
zr, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf)))
if err != nil {
return nil, fmt.Errorf("zip reader: %w", err)
}
for _, f := range zr.File {
if filepath.Base(f.Name) == name && !f.FileInfo().IsDir() {
rc, err := f.Open()
if err != nil {
return nil, fmt.Errorf("open zip entry: %w", err)
}
defer rc.Close()
data, err := io.ReadAll(rc)
if err != nil {
return nil, fmt.Errorf("read binary: %w", err)
}
return data, nil
}
}
return nil, fmt.Errorf("binary %q not found in archive", name)View on GitHub (pinned to 2c0912b6ec)
Solutions
- Log or dump the first bytes of the downloaded body — if it starts with '<' or 'gzip magic' instead of 'PK', the URL or asset is wrong, not the zip parser
- Verify the download: check Content-Length vs bytes read, and validate the release checksum/signature before calling extractBinaryFromZip
- Re-download the release asset from the official URL and retry the update
- If building the release yourself, confirm the packaging step actually produced a .zip containing the binary at the expected entry name
Example fix
// before
buf, err := io.ReadAll(r)
// ...
zr, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf)))
if err != nil {
return nil, fmt.Errorf("zip reader: %w", err)
}
// after: fail fast on obviously-non-zip bodies before parsing
buf, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("read zip data: %w", err)
}
if len(buf) < 4 || !bytes.HasPrefix(buf, []byte("PK\x03\x04")) {
return nil, fmt.Errorf("zip reader: downloaded body is not a zip archive (first bytes: %q)", buf[:min(len(buf), 16)])
} Defensive patterns
Strategy: validation
Validate before calling
// Before calling extractBinaryFromZip, verify the download is a zip and complete.
func verifyZipBody(buf []byte, wantSize int64) error {
if wantSize > 0 && int64(len(buf)) != wantSize {
return fmt.Errorf("short download: got %d bytes, expected %d", len(buf), wantSize)
}
if len(buf) < 4 || !bytes.HasPrefix(buf, []byte("PK\x03\x04")) {
return fmt.Errorf("body is not a zip archive (first bytes: %q)", buf[:min(len(buf), 16)])
}
return nil
} Try / catch
In Go, check errors.Is(err, zip.ErrFormat) after the call to distinguish 'not a zip' from other failures and surface a 're-download the release' hint instead of a generic error.
Prevention
- Always check Content-Length vs bytes read before parsing a downloaded artifact
- Validate release checksums/signatures before extraction
- Never parse proxied responses blindly — reject bodies that start with '<' (HTML)
When it happens
Trigger: Calling the self-update flow (extractBinaryFromZip on a downloaded release stream) where the body is truncated mid-download, is a redirect/login page instead of the asset, or the GitHub asset URL pointed at a non-zip artifact (e.g. a bare .tar.gz or .exe served where a .zip was expected). Also a proxy or AV scanner rewriting the response.
Common situations: CDN/proxy returning a 200 HTML page for the asset URL, flaky network cutting the body early, release publishing mistake (wrong asset uploaded), or a manual test pointing the update URL at a local file that is not a zip.
Related errors
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/1fb86410fb59304d.
Report an issue: GitHub.