{"record":{"id":"eade287aaeb2e238","repo":"router-for-me/CLIProxyAPI","slug":"dynamic-library-filename-must-be-s-or-s","errorCode":null,"errorMessage":"dynamic library filename must be %s or %s","messagePattern":"dynamic library filename must be (.+?) or (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":340,"sourceCode":"\tfor _, file := range reader.File {\n\t\tcleanedName, errClean := cleanZipName(file.Name)\n\t\tif errClean != nil {\n\t\t\treturn nil, 0, errClean\n\t\t}\n\t\tif file.FileInfo().IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tif !regularZipFile(file) {\n\t\t\treturn nil, 0, fmt.Errorf(\"zip entry %s is not a regular file\", file.Name)\n\t\t}\n\t\tif !hasDynamicLibraryExtension(cleanedName) {\n\t\t\tcontinue\n\t\t}\n\t\tif cleanedName != targetName && cleanedName != versionedTargetName {\n\t\t\tif path.Base(cleanedName) == targetName || path.Base(cleanedName) == versionedTargetName {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"target dynamic library must be at zip root\")\n\t\t\t}\n\t\t\treturn nil, 0, fmt.Errorf(\"dynamic library filename must be %s or %s\", targetName, versionedTargetName)\n\t\t}\n\t\tif target != nil {\n\t\t\treturn nil, 0, fmt.Errorf(\"zip contains multiple target dynamic libraries\")\n\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}","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L322-L358","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before (plugin.ID = \"myplugin\", version \"1.0.0\", linux)\nzip plugin.zip libmyplugin_linux.so # rejected\n\n# after\nmv libmyplugin_linux.so myplugin.so\nzip plugin.zip myplugin.so # or name it myplugin-v1.0.0.so","handlingStrategy":"validation","validationCode":"func expectedLibraryNames(id, version, goos string) (string, string) {\n    ext := \".so\"\n    switch strings.ToLower(strings.TrimSpace(goos)) {\n    case \"darwin\", \"mac\", \"macos\", \"osx\":\n        ext = \".dylib\"\n    case \"windows\":\n        ext = \".dll\"\n    }\n    v := strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(version), \"v\"), \"V\")\n    return strings.TrimSpace(id) + ext, strings.TrimSpace(id) + \"-v\" + v + ext\n}\n\nfunc archiveUsesExpectedNames(data []byte, id, version, goos string) error {\n    zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))\n    if err != nil {\n        return err\n    }\n    plain, versioned := expectedLibraryNames(id, version, goos)\n    found := false\n    for _, f := range zr.File {\n        if !f.FileInfo().IsDir() && (f.Name == plain || f.Name == versioned) {\n            found = true\n        }\n    }\n    if !found {\n        return fmt.Errorf(\"archive must contain %s or %s at the root\", plain, versioned)\n    }\n    return nil\n}","typeGuard":"func isLibraryNameError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"dynamic library filename must be\")\n}","tryCatchPattern":"if _, err := pluginstore.InstallArchive(data, plugin, options); err != nil {\n    if isLibraryNameError(err) {\n        // rename the library to <id><ext> or <id>-v<version><ext> and rebuild the archive\n    }\n}","preventionTips":["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"],"tags":["go","plugin-store","zip","packaging","naming"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}