{"record":{"id":"9b8dac9c4e0a4bb8","repo":"flipped-aurora/gin-vue-admin","slug":"error-9b8dac","errorCode":null,"errorMessage":"移除插件注册失败","messagePattern":"移除插件注册失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/auto_code_plugin.go","lineNumber":936,"sourceCode":"\n\t// 1. 删除前端代码\n\tif pluginType == \"web\" || pluginType == \"full\" {\n\t\terr = os.RemoveAll(webDir)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"删除前端插件目录失败\")\n\t\t}\n\t}\n\n\t// 2. 删除后端代码\n\tif pluginType == \"server\" || pluginType == \"full\" {\n\t\terr = os.RemoveAll(serverDir)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"删除后端插件目录失败\")\n\t\t}\n\n\t\t// 移除注册\n\t\tif err = removePluginRegisterImport(pluginName); err != nil {\n\t\t\treturn errors.Wrap(err, \"移除插件注册失败\")\n\t\t}\n\t}\n\n\t// 通过utils 获取 api 菜单 字典\n\tapis, menus, dicts := pluginUtils.GetPluginData(pluginName)\n\n\t// DB 清理阶段脱离请求取消:上面 1/2 的文件删除不可逆且已完成,若客户端此刻\n\t// 断连,请求 ctx 取消会让下面的清理全部快速失败(循环内错误只记日志不中断),\n\t// 菜单/API/字典静默残留;且进程重启后插件不再注册,GetPluginData 拿不到\n\t// 数据,孤儿再也无法通过 Remove 清掉。WithoutCancel 保留链路字段、剥离取消。\n\tcleanupCtx := context.WithoutCancel(ctx)\n\n\t// 3. 删除菜单 (递归删除)\n\tif len(menus) > 0 {\n\t\tfor _, menu := range menus {\n\t\t\tvar dbMenu system.SysBaseMenu\n\t\t\tif err := global.GVA_DB.WithContext(cleanupCtx).Where(\"name = ?\", menu.Name).First(&dbMenu).Error; err == nil {\n\t\t\t\t// 获取该菜单及其所有子菜单的ID","sourceCodeStart":918,"sourceCodeEnd":954,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/auto_code_plugin.go#L918-L954","documentation":"After deleting the plugin's server directory during Remove, the service strips the plugin's import registration from the aggregation file (removePluginRegisterImport, an AST edit of the plugin registry). If that code edit/write fails, the error is wrapped as '移除插件注册失败'. The plugin directories may already be gone, leaving the codebase referencing a deleted package.","triggerScenarios":"removePluginRegisterImport fails because the register file is missing, read-only, the plugin name has no matching import entry, or AST parse/format of the register file fails after directories were already deleted.","commonSituations":"User manually edited the plugin register file (e.g. server/initialize/plugin* or plugin register go file) breaking the expected import format; register file deleted; server process lacks write permission to it; plugin installed but never registered.","solutions":["Manually remove the plugin's import and RegisterPlugin call from the register file, then finish cleanup","Check the register file parses as valid Go (gofmt/go build) and has the expected structure","Ensure the server process can write the register file (permissions)","If the import entry is already absent, treat the plugin as unregistered and verify the build","Re-create the plugin skeleton via the code generator, then uninstall cleanly"],"exampleFix":"// before (manually mangled register file, syntax broken)\n// after: restore valid Go and retry\n$ gofmt -w server/initialize/plugin*.go && go build ./...","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := pluginSvc.Remove(ctx, pluginName, \"full\")\nif err != nil && strings.Contains(err.Error(), \"移除插件注册失败\") {\n    // directories may already be deleted; manually clean the register file\n    // (remove import + RegisterPlugin call) then verify the build:\n    // go build ./...\n}","preventionTips":["Never hand-edit the plugin register file; keep gofmt-clean syntax","Keep the register file under version control so it can be restored","Verify `go build ./...` passes after any manual register edits","Check the plugin actually has a registration entry before removal"],"tags":["go","ast","plugin-registration","plugin-management"],"backgroundTag":"plugin-deregistration-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}