router-for-me/CLIProxyAPI · error
open %s: %w
Error message
open %s: %w
What it means
Wrapped I/O error from opening the selected zip entry inside readTargetLibrary (install.go:351-353). The correct entry was already located, but zip.File.Open() failed while decompressing or reading entry metadata. Because the whole archive is already in memory (zip.NewReader over archiveData), this almost always indicates corruption within the entry's stored data or headers rather than a disk problem.
Source
Thrown at internal/pluginstore/install.go:353
}
if cleanedName != targetName && cleanedName != versionedTargetName {
if path.Base(cleanedName) == targetName || path.Base(cleanedName) == versionedTargetName {
return nil, 0, fmt.Errorf("target dynamic library must be at zip root")
}
return nil, 0, fmt.Errorf("dynamic library filename must be %s or %s", targetName, versionedTargetName)
}
if target != nil {
return nil, 0, fmt.Errorf("zip contains multiple target dynamic libraries")
}
target = file
}
if target == nil {
return nil, 0, fmt.Errorf("zip does not contain %s", targetName)
}
handle, errOpen := target.Open()
if errOpen != nil {
return nil, 0, fmt.Errorf("open %s: %w", targetName, errOpen)
}
defer func() {
if errClose := handle.Close(); errClose != nil {
log.WithError(errClose).Debug("failed to close plugin archive entry")
}
}()
data, errRead := io.ReadAll(handle)
if errRead != nil {
return nil, 0, fmt.Errorf("read %s: %w", targetName, errRead)
}
mode := target.FileInfo().Mode().Perm()
if mode == 0 {
mode = 0o755
}
return data, mode, nil
}
func versionedPluginFileName(id string, version string, goos string) string {View on GitHub (pinned to 78f0c4079e)
Solutions
- Re-download the artifact (clear any HTTP cache) and retry the install so a fresh, complete zip is used
- Confirm the zip is well formed outside the program: unzip -t artifact.zip
- If it reproduces, rebuild the release asset — the stored entry itself is corrupt
Defensive patterns
Strategy: retry
Validate before calling
func zipIntact(archiveData []byte) bool {
r, err := zip.NewReader(bytes.NewReader(archiveData), int64(len(archiveData)))
if err != nil { return false }
for _, f := range r.File {
rc, errOpen := f.Open()
if errOpen != nil { return false }
_, errCopy := io.Copy(io.Discard, rc)
rc.Close()
if errCopy != nil { return false }
}
return true
} Try / catch
var errInstall error
for attempt := 0; attempt < 2; attempt++ {
data, _ := client.DownloadAsset(ctx, asset)
_, errInstall = store.InstallArchive(data, plugin, opts)
if errInstall == nil || !isZipIOError(errInstall) { break }
// re-download with cache bypassed, then retry once
} Prevention
- Always run VerifyChecksum before InstallArchive (the client's Install paths do this for you)
- Download via the provided Client.DownloadAsset rather than ad-hoc HTTP to keep checksum verification in the flow
- Treat repeated entry-open failures as a corrupt asset: re-cut the release
When it happens
Trigger: InstallArchive on an in-memory archive where the entry's compressed stream is truncated or its local header conflicts with the central directory; typically after a partially downloaded or proxy-mangled asset, or a checksum file that was regenerated for a different build so VerifyChecksum passed by coincidence of stale data.
Common situations: Truncated download behind a corporate proxy, a CDN serving a partial response, or a manually re-packed zip whose central directory no longer matches entry offsets.
Related errors
- read %s: %w
- zip contains multiple target dynamic libraries
- zip does not contain %s
- zip entry has empty name
- zip entry %s uses backslash path separators
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/3d1e442078e5a63d.
Report an issue: GitHub.