glzr-io/glazewm · error

Invalid tray menu event

Error message

Invalid tray menu event: {}

What it means

`SystemTrayEvent::from_str` parses menu-item IDs that the system tray sends back over an mpsc channel. Each known ID maps to an enum variant; any unrecognized string (typo, stale ID, event from a different version, or corrupt payload) fails to parse and bails with the raw event text. The library only accepts the exact set of menu IDs it registered.

Solutions

  1. Check the event string in the error message against the supported IDs (show_config_folder, reload_config, toggle_window_animations, run_on_startup, exit) and use an exact match.
  2. Update GlazeWM/config so both sides agree on the menu item IDs.
  3. If adding a new tray item, add a matching arm in `from_str` and register the same ID when building the menu.
  4. Clear stale tray state (restart the app) so old registered menu IDs are not replayed.

Example fix

// before
tray.add_menu_item("reload-conf");
// after
tray.add_menu_item("reload_config");
Defensive patterns

Strategy: validation

Validate before calling

const VALID_IDS: &[&str] = &["show_config_folder","reload_config","toggle_window_animations","run_on_startup","exit"];
fn is_valid_tray_id(id: &str) -> bool { VALID_IDS.contains(&id) }

Type guard

fn parse_tray_event(id: &str) -> Option<SystemTrayEvent> { SystemTrayEvent::from_str(id).ok() }

Try / catch

let event = match SystemTrayEvent::from_str(id) {
  Ok(e) => e,
  Err(_) => { tracing::warn!("Unknown tray event: {}", id); return; }
};

Prevention

When it happens

Trigger: A tray menu click produces an ID string not present in the match arms — e.g. `"show_config_folderx"`, an ID added by a plugin/newer build, or a malformed message delivered on the tray event channel.

Common situations: Running mismatched versions of the tray/IPC components (config referencing a menu item that no longer exists); typos when adding new menu items; custom builds where menu IDs were renamed without updating both sides.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of glzr-io/glazewm@5709ad0a3c (2026-09-08). Data as JSON: /api/errors/b593e805d122be61. Report an issue: GitHub.

Appendix: source

Thrown at packages/wm/src/sys_tray.rs:55

      }
      TrayMenuId::RunOnStartup => write!(f, "run_on_startup"),
      TrayMenuId::Exit => write!(f, "exit"),
    }
  }
}

impl FromStr for TrayMenuId {
  type Err = anyhow::Error;

  fn from_str(event: &str) -> Result<Self, Self::Err> {
    match event {
      "show_config_folder" => Ok(Self::ShowConfigFolder),
      "reload_config" => Ok(Self::ReloadConfig),
      #[cfg(target_os = "windows")]
      "toggle_window_animations" => Ok(Self::ToggleWindowAnimations),
      "run_on_startup" => Ok(Self::RunOnStartup),
      "exit" => Ok(Self::Exit),
      _ => anyhow::bail!("Invalid tray menu event: {}", event),
    }
  }
}

pub struct SystemTray {
  pub config_reload_rx: mpsc::UnboundedReceiver<()>,
  pub exit_rx: mpsc::UnboundedReceiver<()>,
  _icon_thread: Option<std::thread::JoinHandle<()>>,
  _tray_icon: ThreadBound<TrayIcon>,
}

impl SystemTray {
  /// Install the system tray on the main thread after the run loop starts.
  pub fn new(
    config_path: &Path,
    dispatcher: Dispatcher,
  ) -> anyhow::Result<Self> {
    let (exit_tx, exit_rx) = mpsc::unbounded_channel();

View on GitHub (pinned to 5709ad0a3c)