router-for-me/CLIProxyAPI · error
dynamic library filename must be %s or %s
Error message
dynamic library filename must be %s or %s
What it means
Thrown by readTargetLibrary when an archive entry has a dynamic-library extension but its name matches neither the plain target name (trimmed id + platform extension: .dylib/.dll/.so) nor the versioned name (id + "-v" + normalized version + extension), and its basename does not match either — i.e. the library is present but named differently than the plugin ID/version demand. The message includes both accepted names.
Source
Thrown at internal/pluginstore/install.go:340
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)
}
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")
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Rename the library inside the archive to exactly <id><ext> or <id>-v<version><ext> for the target platform
- Align the plugin ID in your metadata with the artifact filename actually built
- Double-check options.GOOS (and the resulting extension) matches the archive contents
Example fix
# before (plugin.ID = "myplugin", version "1.0.0", linux) zip plugin.zip libmyplugin_linux.so # rejected # after mv libmyplugin_linux.so myplugin.so zip plugin.zip myplugin.so # or name it myplugin-v1.0.0.so
Defensive patterns
Strategy: validation
Validate before calling
func expectedLibraryNames(id, version, goos string) (string, string) {
ext := ".so"
switch strings.ToLower(strings.TrimSpace(goos)) {
case "darwin", "mac", "macos", "osx":
ext = ".dylib"
case "windows":
ext = ".dll"
}
v := strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(version), "v"), "V")
return strings.TrimSpace(id) + ext, strings.TrimSpace(id) + "-v" + v + ext
}
func archiveUsesExpectedNames(data []byte, id, version, goos string) error {
zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return err
}
plain, versioned := expectedLibraryNames(id, version, goos)
found := false
for _, f := range zr.File {
if !f.FileInfo().IsDir() && (f.Name == plain || f.Name == versioned) {
found = true
}
}
if !found {
return fmt.Errorf("archive must contain %s or %s at the root", plain, versioned)
}
return nil
} Type guard
func isLibraryNameError(err error) bool {
return err != nil && strings.Contains(err.Error(), "dynamic library filename must be")
} Try / catch
if _, err := pluginstore.InstallArchive(data, plugin, options); err != nil {
if isLibraryNameError(err) {
// rename the library to <id><ext> or <id>-v<version><ext> and rebuild the archive
}
} Prevention
- Name build outputs exactly <id>-v<version><ext> in the build script
- Keep the plugin ID stable once published; renaming requires re-shipping artifacts
- Verify GOOS/GOARCH passed in InstallOptions matches the archive being installed
When it happens
Trigger: Archive contains 'libmyplugin.so' or 'myplugin_linux_amd64.so' when the plugin ID is 'myplugin' and version '1.0.0' — expected exactly 'myplugin.so' or 'myplugin-v1.0.0.so'. Also mismatched platform extensions (a .so inside while running on darwin where .dylib is expected).
Common situations: Build system names the artifact with its own convention (lib prefix, platform suffix) that differs from the registry plugin ID; plugin ID changed without rebuilding artifacts; cross-platform archives where each platform names libraries differently; wrong GOOS passed in InstallOptions so the expected extension differs.
Related errors
- target dynamic library must be at zip root
- open zip: %w
- zip entry %s is not a regular file
- checksum for %s not found
- download artifact: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/eade287aaeb2e238.
Report an issue: GitHub.