{"record":{"id":"d5ef088dc97612c3","repo":"flipped-aurora/gin-vue-admin","slug":"plugin-archive-contains-multiple-server-plugins","errorCode":null,"errorMessage":"plugin archive contains multiple server plugins","messagePattern":"plugin archive contains multiple server plugins","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/auto_code_plugin.go","lineNumber":293,"sourceCode":"\t\tif err != nil {\n\t\t\treturn pluginArchive{}, err\n\t\t}\n\t\tparts := strings.Split(filepath.ToSlash(relativePath), \"/\")\n\t\tfor index := 0; index+2 < len(parts); index++ {\n\t\t\tif parts[index+1] != \"plugin\" || (parts[index] != \"server\" && parts[index] != \"web\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tname := parts[index+2]\n\t\t\tif err := utils.ValidatePluginName(name); err != nil {\n\t\t\t\treturn pluginArchive{}, err\n\t\t\t}\n\t\t\tpart := &pluginArchivePart{\n\t\t\t\troot: filepath.Join(tempDir, filepath.FromSlash(strings.Join(parts[:index+2], \"/\"))),\n\t\t\t\tname: name,\n\t\t\t}\n\t\t\tif parts[index] == \"server\" {\n\t\t\t\tif archive.server != nil && (archive.server.name != part.name || archive.server.root != part.root) {\n\t\t\t\t\treturn pluginArchive{}, errors.New(\"plugin archive contains multiple server plugins\")\n\t\t\t\t}\n\t\t\t\tarchive.server = part\n\t\t\t} else {\n\t\t\t\tif archive.web != nil && (archive.web.name != part.name || archive.web.root != part.root) {\n\t\t\t\t\treturn pluginArchive{}, errors.New(\"plugin archive contains multiple web plugins\")\n\t\t\t\t}\n\t\t\t\tarchive.web = part\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\tif archive.server == nil && archive.web == nil {\n\t\treturn pluginArchive{}, errors.New(\"invalid plugin archive\")\n\t}\n\treturn archive, nil\n}\n\nfunc pluginInstallRoot(component, parentPlugin string) (string, error) {","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/auto_code_plugin.go#L275-L311","documentation":"While scanning the uploaded archive, the installer collects `server/<name>/` directories as candidate server plugin parts. If it finds two distinct server plugin roots (different name or path), it aborts, because a single archive must carry at most one server plugin.","triggerScenarios":"Installing a sub-plugin archive that contains more than one unique `server/...` plugin directory, e.g. `server/plugin/a` and `server/plugin/b`, or two paths with different names under the server root. findPluginArchive throws when the second distinct server part is encountered.","commonSituations":"Zipping an entire `plugin/` folder containing several plugins instead of one; accidentally including a nested or copied plugin directory; re-zipping an extracted archive that already had leftovers from a previous plugin.","solutions":["Rebuild the archive so it contains exactly one server plugin directory.","Remove extra server plugin directories from the zip before uploading.","If multiple plugins must be installed, upload them as separate archives in separate install operations."],"exampleFix":"// before (archive layout)\nserver/plugin/foo/\nserver/plugin/bar/\nweb/src/plugin/foo/\n// after\nserver/plugin/foo/\nweb/src/plugin/foo/\n// (bar must be installed via its own archive)","handlingStrategy":"validation","validationCode":"const serverDirs = new Set(entries.filter(e => /(^|\\/)server\\/[^/]+\\//.test(e)).map(e => e.split('/')[1]))\nif (serverDirs.size > 1) throw new Error(`archive has ${serverDirs.size} server plugins: ${[...serverDirs]}`)","typeGuard":null,"tryCatchPattern":"try {\n  await installPlugin(archive)\n} catch (e) {\n  if (e.message.includes('multiple server plugins')) {\n    showHint('Split the archive: one server plugin per zip.')\n  } else { throw e }\n}","preventionTips":["Zip only the single plugin directory, never the whole plugin root.","Clean staging/extract folders before rebuilding an archive to avoid leftovers.","Add a CI packaging step that asserts exactly one server plugin per artifact."],"tags":["plugin-install","archive-validation"],"backgroundTag":"plugin-archive-structure-invalid","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}