Foundry376/Mailspring · error

Menu items have colliding keys

Error message

Menu items have colliding keys

What it means

Warning while rendering Menu items: two items produced the same itemKey, so React reconciliation can swap/duplicate rows and selectedItemKey identifies the wrong item. Duplicate data (e.g. two contacts with the same email rendered as separate rows) is the usual cause.

Solutions

  1. Deduplicate the items list before rendering
  2. Make itemKey unique by combining fields (id + index or email + label)
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at app/src/components/menu.tsx:408 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Foundry376/Mailspring@648c685d60 (2026-09-03). Data as JSON: /api/errors/3e87c0b48ed7d5b9. Report an issue: GitHub.

Appendix: source

Thrown at app/src/components/menu.tsx:408

      if (React.isValidElement(content) && content.type === MenuItem) {
        return content;
      }

      const onMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
        event.preventDefault();
        const key = this.props.itemKey(item);
        this.setState({ selectedIndex: i, selectedItemKey: key });
        if (this.props.onSelect) {
          return this.props.onSelect(item);
        }
      };

      const key = this.props.itemKey(item);
      if (!key) {
        console.warn('Menu parent did not return an itemKey for item', item);
      }
      if (seenItemKeys[key]) {
        console.warn({ 'Menu items have colliding keys': item }, seenItemKeys[key]);
      }
      seenItemKeys[key] = item;

      return (
        <MenuItem
          key={key}
          id={this.props.listboxId ? `${this.props.listboxId}-option-${key}` : undefined}
          role={this.props.listboxId ? 'option' : undefined}
          onMouseDown={onMouseDown}
          checked={this.props.itemChecked && this.props.itemChecked(item)}
          content={content}
          selected={this.state.selectedIndex === i}
          suppressAriaSelected={!!this.props.onActiveDescendantChange}
        />
      );
    });

    const contentClass = classNames({

View on GitHub (pinned to 648c685d60)