router-for-me/CLIProxyAPI · error
zip entry %s is not a regular file
Error message
zip entry %s is not a regular file
What it means
Thrown by readTargetLibrary while scanning the archive: a zip entry that is neither a directory nor a regular file (regularZipFile false — e.g. a symlink, device, or FIFO mode). The extractor refuses non-regular entries to prevent symlink-based attacks and unextractable special files; it scans every entry with a dynamic-library extension, so an unexpected mode on any matching entry aborts the install.
Source
Thrown at internal/pluginstore/install.go:331
return "", fmt.Errorf("invalid plugin version %q", version)
}
return filepath.Join(options.PluginsDir, options.GOOS, options.GOARCH, versionedPluginFileName(id, version, options.GOOS)), nil
}
func readTargetLibrary(reader *zip.Reader, id string, version string, goos string) ([]byte, os.FileMode, error) {
targetName := strings.TrimSpace(id) + pluginExtension(goos)
versionedTargetName := versionedPluginFileName(id, version, goos)
var target *zip.File
for _, file := range reader.File {
cleanedName, errClean := cleanZipName(file.Name)
if errClean != nil {
return nil, 0, errClean
}
if file.FileInfo().IsDir() {
continue
}
if !regularZipFile(file) {
return nil, 0, fmt.Errorf("zip entry %s is not a regular file", file.Name)
}
if !hasDynamicLibraryExtension(cleanedName) {
continue
}
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)
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Rebuild the archive with real file contents instead of symlinks (zip without -y, or cp -L before zipping)
- If consuming third-party artifacts, reject the artifact at the checksum/verification layer and report it upstream
- Inspect with 'zipinfo -l' to find the non-regular entry
Example fix
# before ln -s /opt/lib/real.so myplugin.so && zip -y plugin.zip myplugin.so # symlink stored # after cp /opt/lib/real.so myplugin.so && zip plugin.zip myplugin.so # regular file stored
Defensive patterns
Strategy: validation
Validate before calling
func archiveHasOnlyRegularEntries(data []byte) error {
zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return err
}
for _, f := range zr.File {
mode := f.FileInfo().Mode()
if mode.IsDir() {
continue
}
if !mode.IsRegular() {
return fmt.Errorf("entry %s has mode %v (not a regular file)", f.Name, mode)
}
}
return nil
} Type guard
func isNonRegularZipEntry(err error) bool {
return err != nil && strings.Contains(err.Error(), "is not a regular file")
} Try / catch
if _, err := pluginstore.InstallArchive(data, plugin, options); err != nil {
if isNonRegularZipEntry(err) {
// reject artifact: rebuild archive without symlinks (zip without -y)
}
} Prevention
- Never zip with symlink preservation (-y) for distributable artifacts
- Add a packaging CI step asserting all entries are regular files
- Treat non-regular entries in third-party artifacts as suspicious (possible tampering)
When it happens
Trigger: An archive containing a symlink named like the plugin library (e.g. 'myplugin.so -> ../../system.so'), or entries created by archive tools that record unusual unix modes; triggered as soon as such an entry has a dynamic library extension.
Common situations: Repacking plugins with 'zip -y' (preserves symlinks) on Linux; malicious or tampered artifacts attempting path redirection; archives built by nonstandard tooling emitting odd external attributes.
Related errors
- open zip: %w
- target dynamic library must be at zip root
- dynamic library filename must be %s or %s
- invalid auth file name
- plugin store url must not contain credentials
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/81bd73e8de37b354.
Report an issue: GitHub.