{"record":{"id":"9cfefe2c75500345","repo":"flipped-aurora/gin-vue-admin","slug":"error-9cfefe","errorCode":null,"errorMessage":"父菜单不存在","messagePattern":"父菜单不存在","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_menu.go","lineNumber":149,"sourceCode":"//@author: [piexlmax](https://github.com/piexlmax)\n//@function: AddBaseMenu\n//@description: 添加基础路由\n//@param: menu model.SysBaseMenu\n//@return: error\n\nfunc (menuService *MenuService) AddBaseMenu(ctx context.Context, menu system.SysBaseMenu) error {\n\treturn global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {\n\t\t// 检查name是否重复\n\t\tif !errors.Is(tx.Where(\"name = ?\", menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {\n\t\t\treturn errors.New(\"存在重复name，请修改name\")\n\t\t}\n\n\t\tif menu.ParentId != 0 {\n\t\t\t// 检查父菜单是否存在\n\t\t\tvar parentMenu system.SysBaseMenu\n\t\t\tif err := tx.First(&parentMenu, menu.ParentId).Error; err != nil {\n\t\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t\treturn errors.New(\"父菜单不存在\")\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// 检查父菜单下现有子菜单数量\n\t\t\tvar existingChildrenCount int64\n\t\t\terr := tx.Model(&system.SysBaseMenu{}).Where(\"parent_id = ?\", menu.ParentId).Count(&existingChildrenCount).Error\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// 如果父菜单原本是叶子菜单（没有子菜单），现在要变成枝干菜单，需要清空其权限分配\n\t\t\tif existingChildrenCount == 0 {\n\t\t\t\t// 检查父菜单是否被其他角色设置为首页\n\t\t\t\tvar defaultRouterCount int64\n\t\t\t\terr := tx.Model(&system.SysAuthority{}).Where(\"default_router = ?\", parentMenu.Name).Count(&defaultRouterCount).Error\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_menu.go#L131-L167","documentation":"In AddBaseMenu, when a new menu specifies a non-zero ParentId, the service looks up the parent SysBaseMenu inside the transaction. If gorm returns ErrRecordNotFound the transaction rolls back with this error, since a child menu cannot be attached to a nonexistent parent.","triggerScenarios":"Calling AddBaseMenu with menu.ParentId set to an ID that does not exist in sys_base_menu (deleted parent, stale client cache, or fabricated ID).","commonSituations":"Client UI cached an old parent list after the parent was deleted; frontend sends parentId=0 sentinel misused as a real ID; importing menu trees out of order so children are inserted before parents.","solutions":["Verify the ParentId exists: SELECT id,name FROM sys_base_menu WHERE id = <parentId>.","Refresh the parent menu dropdown/tree in the client before resubmitting.","Set ParentId to 0 if the menu should be a top-level menu.","When importing, insert parent menus before their children."],"exampleFix":"// before\nawait addMenu({ name: 'btnDelete', parentId: 9999 }) // parent deleted\n// after\nconst parents = await getMenuList()\nconst parentId = parents.find(m => m.name === 'user').ID\nawait addMenu({ name: 'btnDelete', parentId })","handlingStrategy":"validation","validationCode":"const tree = (await menuApi.getMenuList()).data.list\nconst parentExists = newMenu.parentId === 0 || tree.some(m => m.ID === newMenu.parentId)\nif (!parentExists) {\n  alert('所选父菜单不存在，请重新选择')\n  return\n}","typeGuard":null,"tryCatchPattern":"try {\n  await menuApi.addMenu(newMenu)\n} catch (e) {\n  if (e.msg === '父菜单不存在') {\n    await reloadMenuTree() // refresh parents, let user re-pick\n  } else throw e\n}","preventionTips":["Refresh the parent-menu selector from the server each time the form opens.","Insert parents before children in import scripts.","Treat parentId=0 strictly as 'top-level', never as a real row id."],"tags":["go","gin-vue-admin","menu","foreign-key","validation"],"backgroundTag":"parent-record-not-found","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}