{"record":{"id":"cfc70e9d4f7b363e","repo":"router-for-me/CLIProxyAPI","slug":"read-s-w","errorCode":null,"errorMessage":"read %s: %w","messagePattern":"read (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":362,"sourceCode":"\t\t}\n\t\ttarget = file\n\t}\n\tif target == nil {\n\t\treturn nil, 0, fmt.Errorf(\"zip does not contain %s\", targetName)\n\t}\n\n\thandle, errOpen := target.Open()\n\tif errOpen != nil {\n\t\treturn nil, 0, fmt.Errorf(\"open %s: %w\", targetName, errOpen)\n\t}\n\tdefer func() {\n\t\tif errClose := handle.Close(); errClose != nil {\n\t\t\tlog.WithError(errClose).Debug(\"failed to close plugin archive entry\")\n\t\t}\n\t}()\n\tdata, errRead := io.ReadAll(handle)\n\tif errRead != nil {\n\t\treturn nil, 0, fmt.Errorf(\"read %s: %w\", targetName, errRead)\n\t}\n\tmode := target.FileInfo().Mode().Perm()\n\tif mode == 0 {\n\t\tmode = 0o755\n\t}\n\treturn data, mode, nil\n}\n\nfunc versionedPluginFileName(id string, version string, goos string) string {\n\treturn strings.TrimSpace(id) + \"-v\" + normalizeVersion(version) + pluginExtension(goos)\n}\n\nfunc cleanZipName(name string) (string, error) {\n\tif strings.TrimSpace(name) == \"\" {\n\t\treturn \"\", fmt.Errorf(\"zip entry has empty name\")\n\t}\n\tif strings.Contains(name, `\\`) {\n\t\treturn \"\", fmt.Errorf(\"zip entry %s uses backslash path separators\", name)","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L344-L380","documentation":"Wrapped I/O error from io.ReadAll on the opened zip entry in readTargetLibrary (install.go:360-362). The entry opened fine but reading its decompressed bytes failed — classically because the entry's declared CRC/size does not match the actual data, which archive/zip verifies at stream end. Indicates corrupted or tampered entry content.","triggerScenarios":"InstallArchive where the target library entry's decompressed bytes fail CRC validation mid-read (zip.ErrChecksum wrapped underneath), e.g. a bit-flipped download or an asset edited after the checksums.txt was generated.","commonSituations":"Flaky network truncating the body, a mirrored artifact modified in transit, or a release pipeline that updates the binary inside the zip without refreshing checksums (so VerifyChecksum on the outer file passes but the inner entry is inconsistent).","solutions":["Re-download the asset and retry","Validate the entry independently: unzip -t artifact.zip to surface CRC errors","If self-publishing, rebuild the archive and checksums together in one release job so they cannot diverge"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"func zipCRCOK(archiveData []byte) error {\n    r, err := zip.NewReader(bytes.NewReader(archiveData), int64(len(archiveData)))\n    if err != nil { return err }\n    for _, f := range r.File {\n        rc, errOpen := f.Open()\n        if errOpen != nil { return errOpen }\n        _, errCopy := io.Copy(io.Discard, rc)\n        rc.Close()\n        if errCopy != nil { return errCopy } // surfaces CRC mismatch\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := store.InstallArchive(data, plugin, opts); err != nil {\n    var zipErr *zip.ChecksumError // may surface wrapped under the read error\n    if errors.As(err, &zipErr) || strings.Contains(err.Error(), \"read \"+plugin.ID) {\n        // re-download once; if it persists the asset itself is bad\n    }\n}","preventionTips":["Generate checksums.txt and the zip in the same atomic release step","Never edit files inside a published zip","Verify downloads (VerifyChecksum) before installing"],"tags":["pluginstore","zip","io","crc","corruption"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}