{"record":{"id":"8ec8f56992178340","repo":"flipped-aurora/gin-vue-admin","slug":"error-8ec8f5","errorCode":null,"errorMessage":"删除后端插件目录失败","messagePattern":"删除后端插件目录失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/auto_code_plugin.go","lineNumber":931,"sourceCode":"\t\tserverDir, err = pluginPath(global.GVA_CONFIG.AutoCode.Server, pluginName)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\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. 删除菜单 (递归删除)","sourceCodeStart":913,"sourceCodeEnd":949,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/auto_code_plugin.go#L913-L949","documentation":"In AutoCodePluginService.Remove, for pluginType 'server' or 'full', os.RemoveAll(serverDir) deletes the plugin's backend directory under server/plugin/<name>. Failure is wrapped as '删除后端插件目录失败'. This happens after the frontend step and before the plugin registration import is removed.","triggerScenarios":"Calling Remove for a server/full plugin while the server/plugin/<name> directory cannot be deleted: permissions, binary/compiled artifacts locked by a running build, or read-only mount.","commonSituations":"Go build cache or a compiled plugin binary still running/locked; container with read-only server/plugin volume; wrong user (server not run as directory owner); NFS/Windows share lock issues.","solutions":["Stop any process using files under server/plugin/<pluginName> and retry","Fix ownership/permissions: chown -R or chmod u+w server/plugin/<pluginName>","Check that the volume containing server/plugin is writable","If partially deleted, clean remnants manually then retry Remove","Verify AutoCode.Server config points at the real server root"],"exampleFix":"// before\n$ rm -rf server/plugin/foo  # permission denied\n// after\n$ sudo chown -R $(whoami) server/plugin/foo && retry","handlingStrategy":"validation","validationCode":"serverDir := filepath.Join(\"server/plugin\", pluginName)\nif fi, err := os.Stat(serverDir); err == nil && !fi.IsDir() {\n    return errors.New(\"plugin path is not a directory\")\n}\nif err := unix.Access(filepath.Dir(serverDir), unix.W_OK); err != nil {\n    return fmt.Errorf(\"cannot delete under server/plugin: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := pluginSvc.Remove(ctx, pluginName, \"server\")\nif err != nil && strings.Contains(err.Error(), \"删除后端插件目录失败\") {\n    log.Printf(\"underlying: %v\", errors.Unwrap(err)) // fs.ErrPermission / fs.ErrNotExist\n}","preventionTips":["Ensure no compiled binary or build process holds server/plugin files","Mount code volumes read-write in containers","Verify AutoCode.Server config points to the correct server root","Clean partially deleted plugin dirs before retrying"],"tags":["filesystem","plugin-management","go","os-removeall"],"backgroundTag":"file-delete-permission-denied","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}