flipped-aurora/gin-vue-admin · error
父菜单不存在
Error message
父菜单不存在
What it means
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.
Source
Thrown at server/service/system/sys_menu.go:149
//@author: [piexlmax](https://github.com/piexlmax)
//@function: AddBaseMenu
//@description: 添加基础路由
//@param: menu model.SysBaseMenu
//@return: error
func (menuService *MenuService) AddBaseMenu(ctx context.Context, menu system.SysBaseMenu) error {
return global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 检查name是否重复
if !errors.Is(tx.Where("name = ?", menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
return errors.New("存在重复name,请修改name")
}
if menu.ParentId != 0 {
// 检查父菜单是否存在
var parentMenu system.SysBaseMenu
if err := tx.First(&parentMenu, menu.ParentId).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("父菜单不存在")
}
return err
}
// 检查父菜单下现有子菜单数量
var existingChildrenCount int64
err := tx.Model(&system.SysBaseMenu{}).Where("parent_id = ?", menu.ParentId).Count(&existingChildrenCount).Error
if err != nil {
return err
}
// 如果父菜单原本是叶子菜单(没有子菜单),现在要变成枝干菜单,需要清空其权限分配
if existingChildrenCount == 0 {
// 检查父菜单是否被其他角色设置为首页
var defaultRouterCount int64
err := tx.Model(&system.SysAuthority{}).Where("default_router = ?", parentMenu.Name).Count(&defaultRouterCount).Error
if err != nil {
return errView on GitHub (pinned to 3136500ef3)
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.
Example fix
// before
await addMenu({ name: 'btnDelete', parentId: 9999 }) // parent deleted
// after
const parents = await getMenuList()
const parentId = parents.find(m => m.name === 'user').ID
await addMenu({ name: 'btnDelete', parentId }) Defensive patterns
Strategy: validation
Validate before calling
const tree = (await menuApi.getMenuList()).data.list
const parentExists = newMenu.parentId === 0 || tree.some(m => m.ID === newMenu.parentId)
if (!parentExists) {
alert('所选父菜单不存在,请重新选择')
return
} Try / catch
try {
await menuApi.addMenu(newMenu)
} catch (e) {
if (e.msg === '父菜单不存在') {
await reloadMenuTree() // refresh parents, let user re-pick
} else throw e
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/9cfefe2c75500345.
Report an issue: GitHub.