{"record":{"id":"1fb86410fb59304d","repo":"multica-ai/multica","slug":"zip-reader-w","errorCode":null,"errorMessage":"zip reader: %w","messagePattern":"zip reader: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/update.go","lineNumber":521,"sourceCode":"\t\t\t\treturn nil, fmt.Errorf(\"read binary: %w\", err)\n\t\t\t}\n\t\t\treturn data, nil\n\t\t}\n\t}\n}\n\n// extractBinaryFromZip reads a .zip stream and returns the contents of the\n// named file entry. The zip format requires random access, so the full archive\n// is buffered in memory.\nfunc extractBinaryFromZip(r io.Reader, name string) ([]byte, error) {\n\tbuf, err := io.ReadAll(r)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read zip data: %w\", err)\n\t}\n\n\tzr, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf)))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"zip reader: %w\", err)\n\t}\n\n\tfor _, f := range zr.File {\n\t\tif filepath.Base(f.Name) == name && !f.FileInfo().IsDir() {\n\t\t\trc, err := f.Open()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"open zip entry: %w\", err)\n\t\t\t}\n\t\t\tdefer rc.Close()\n\n\t\t\tdata, err := io.ReadAll(rc)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"read binary: %w\", err)\n\t\t\t}\n\t\t\treturn data, nil\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"binary %q not found in archive\", name)","sourceCodeStart":503,"sourceCodeEnd":539,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/update.go#L503-L539","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nbuf, err := io.ReadAll(r)\n// ...\nzr, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf)))\nif err != nil {\n    return nil, fmt.Errorf(\"zip reader: %w\", err)\n}\n\n// after: fail fast on obviously-non-zip bodies before parsing\nbuf, err := io.ReadAll(r)\nif err != nil {\n    return nil, fmt.Errorf(\"read zip data: %w\", err)\n}\nif len(buf) < 4 || !bytes.HasPrefix(buf, []byte(\"PK\\x03\\x04\")) {\n    return nil, fmt.Errorf(\"zip reader: downloaded body is not a zip archive (first bytes: %q)\", buf[:min(len(buf), 16)])\n}","handlingStrategy":"validation","validationCode":"// Before calling extractBinaryFromZip, verify the download is a zip and complete.\nfunc verifyZipBody(buf []byte, wantSize int64) error {\n    if wantSize > 0 && int64(len(buf)) != wantSize {\n        return fmt.Errorf(\"short download: got %d bytes, expected %d\", len(buf), wantSize)\n    }\n    if len(buf) < 4 || !bytes.HasPrefix(buf, []byte(\"PK\\x03\\x04\")) {\n        return fmt.Errorf(\"body is not a zip archive (first bytes: %q)\", buf[:min(len(buf), 16)])\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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)"],"tags":["zip","self-update","download","io"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}