nocobase/nocobase · error · Error

Plugin settings menuKey does not exist: menuKey=${options.me

Error message

Plugin settings menuKey does not exist: menuKey=${options.menuKey}

What it means

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.

Source

Thrown at packages/core/client-v2/src/PluginSettingsManager.ts:206

    this.menus[menuName] = nextMenu;
    this.syncMenuRoute(nextMenu);
    this.clearCache();
  }

  /**
   * 注册或更新 page item。
   *
   * @param {PluginSettingsPageItemOptions} options page 配置
   * @returns {void}
   * @throws {Error} 当 menu 不存在或路径冲突时抛错
   */
  addPageTabItem(options: PluginSettingsPageItemOptions) {
    this.assertMenuKey(options.menuKey, 'menuKey');

    const menu = this.menus[options.menuKey];

    if (!menu) {
      throw new Error(`Plugin settings menuKey does not exist: menuKey=${options.menuKey}`);
    }

    const pageName = this.getPageName(options.menuKey, options.key);
    const nextPage: InternalPageItemRecord = {
      ...this.pages[pageName],
      ...options,
      menuKey: options.menuKey,
      key: options.key,
      name: pageName,
    };

    this.assertIndexConflict(nextPage);
    this.assertPathConflict(pageName, this.getRoutePath(pageName));

    this.pages[pageName] = nextPage;
    this.syncPageRoute(nextPage);
    this.clearCache();
  }

View on GitHub (pinned to fa42722fef)

Solutions

  1. Ensure the menu is registered first: call registerMenu (or equivalent) with the same menuKey before addPageTabItem
  2. Log/inspect available keys (Object.keys(menus)) and fix typos in options.menuKey
  3. Check plugin load order/dependencies so the menu-owning plugin initializes first
  4. If upgrading, update the menuKey to the new name from the owning plugin's changelog

Example fix

// before
pluginSettingsManager.addPageTabItem({ menuKey: 'ai-settings-typo', key: 'model', title: 'Model' });

// after
pluginSettingsManager.registerMenu({ name: 'ai-settings', title: 'AI Settings' });
pluginSettingsManager.addPageTabItem({ menuKey: 'ai-settings', key: 'model', title: 'Model' });
Defensive patterns

Strategy: validation

Validate before calling

if (!pluginSettingsManager.menus[menuKey]) {
  throw new Error(`Register menu "${menuKey}" before addPageTabItem`);
}
pluginSettingsManager.addPageTabItem({ menuKey, key, title });

Type guard

function menuExists(mgr: PluginSettingsManager, menuKey: string): boolean {
  return menuKey in (mgr as unknown as { menus: Record<string, unknown> }).menus;
}

Try / catch

try {
  pluginSettingsManager.addPageTabItem({ menuKey, key: 'model', title: 'Model' });
} catch (err) {
  if (String(err.message).startsWith('Plugin settings menuKey does not exist')) {
    pluginSettingsManager.registerMenu({ name: menuKey, title: 'AI Settings' });
    pluginSettingsManager.addPageTabItem({ menuKey, key: 'model', title: 'Model' });
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01). Data as JSON: /api/errors/01c0d8d8d09a2905. Report an issue: GitHub.