{"record":{"id":"01c0d8d8d09a2905","repo":"nocobase/nocobase","slug":"plugin-settings-menukey-does-not-exist-menukey","errorCode":null,"errorMessage":"Plugin settings menuKey does not exist: menuKey=${options.menuKey}","messagePattern":"Plugin settings menuKey does not exist: menuKey=(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/client-v2/src/PluginSettingsManager.ts","lineNumber":206,"sourceCode":"    this.menus[menuName] = nextMenu;\n    this.syncMenuRoute(nextMenu);\n    this.clearCache();\n  }\n\n  /**\n   * 注册或更新 page item。\n   *\n   * @param {PluginSettingsPageItemOptions} options page 配置\n   * @returns {void}\n   * @throws {Error} 当 menu 不存在或路径冲突时抛错\n   */\n  addPageTabItem(options: PluginSettingsPageItemOptions) {\n    this.assertMenuKey(options.menuKey, 'menuKey');\n\n    const menu = this.menus[options.menuKey];\n\n    if (!menu) {\n      throw new Error(`Plugin settings menuKey does not exist: menuKey=${options.menuKey}`);\n    }\n\n    const pageName = this.getPageName(options.menuKey, options.key);\n    const nextPage: InternalPageItemRecord = {\n      ...this.pages[pageName],\n      ...options,\n      menuKey: options.menuKey,\n      key: options.key,\n      name: pageName,\n    };\n\n    this.assertIndexConflict(nextPage);\n    this.assertPathConflict(pageName, this.getRoutePath(pageName));\n\n    this.pages[pageName] = nextPage;\n    this.syncPageRoute(nextPage);\n    this.clearCache();\n  }","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/core/client-v2/src/PluginSettingsManager.ts#L188-L224","documentation":"PluginSettingsManager.addPageTabItem() registers a tab item under an existing settings menu. Before registering, it requires this.menus[options.menuKey] to exist; if the menuKey was never registered (or was registered with a different key), it throws this error. This prevents attaching tab items to a nonexistent menu whose page routing could not be resolved.","triggerScenarios":"Calling addPageTabItem({ menuKey: 'X', ... }) (e.g. from registerPluginAISettingsPages) before registerMenu/registerPage was called with menuKey 'X', or with a typo'd/stale key after the menu was renamed or removed.","commonSituations":"Plugin registration order issues where another plugin's settings page is added before the menu owner plugin initializes; menu key renamed in a version upgrade while consumer plugins still use the old key; copy-paste typos in menuKey strings.","solutions":["Ensure the menu is registered first: call registerMenu (or equivalent) with the same menuKey before addPageTabItem","Log/inspect available keys (Object.keys(menus)) and fix typos in options.menuKey","Check plugin load order/dependencies so the menu-owning plugin initializes first","If upgrading, update the menuKey to the new name from the owning plugin's changelog"],"exampleFix":"// before\npluginSettingsManager.addPageTabItem({ menuKey: 'ai-settings-typo', key: 'model', title: 'Model' });\n\n// after\npluginSettingsManager.registerMenu({ name: 'ai-settings', title: 'AI Settings' });\npluginSettingsManager.addPageTabItem({ menuKey: 'ai-settings', key: 'model', title: 'Model' });","handlingStrategy":"validation","validationCode":"if (!pluginSettingsManager.menus[menuKey]) {\n  throw new Error(`Register menu \"${menuKey}\" before addPageTabItem`);\n}\npluginSettingsManager.addPageTabItem({ menuKey, key, title });","typeGuard":"function menuExists(mgr: PluginSettingsManager, menuKey: string): boolean {\n  return menuKey in (mgr as unknown as { menus: Record<string, unknown> }).menus;\n}","tryCatchPattern":"try {\n  pluginSettingsManager.addPageTabItem({ menuKey, key: 'model', title: 'Model' });\n} catch (err) {\n  if (String(err.message).startsWith('Plugin settings menuKey does not exist')) {\n    pluginSettingsManager.registerMenu({ name: menuKey, title: 'AI Settings' });\n    pluginSettingsManager.addPageTabItem({ menuKey, key: 'model', title: 'Model' });\n  } else throw err;\n}","preventionTips":["Always registerMenu before addPageTabItem in the same plugin","Extract menuKey strings into shared constants instead of inline literals","Verify plugin load order so menu owners initialize before consumers","After version upgrades, grep for old menu keys and update them to renamed values"],"tags":["plugin-settings","client-v2","registration-order","typo"],"backgroundTag":"unknown-menu-key","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}