Hmbown/CodeWhale · error

invalid or duplicate notification event

Error message

invalid or duplicate notification event

What it means

This error is thrown by NotificationConfigUpdate::validate when any entry in an EventSoundEvents list is not a recognized notification event name, or when the same event name appears more than once in the list. NotificationEvent::parse must accept each name, and each must be unique within the list.

Solutions

  1. Check each name against the accepted NotificationEvent names (exact match, correct spelling/case).
  2. Deduplicate the list before constructing the update.
  3. If an event name was renamed in a newer version, update the config to the new name.
  4. Use NotificationEvent::parse in your own code to validate names before building the list.

Example fix

// before
NotificationConfigUpdate::EventSoundEvents(vec!["turn-complete".into(), "turn-complete".into()])
// after
NotificationConfigUpdate::EventSoundEvents(vec!["turn-complete".into(), "error".into()])
Defensive patterns

Strategy: validation

Validate before calling

fn valid_event_names(events: &[String]) -> bool {
    events.iter().all(|e| NotificationEvent::parse(e).is_some())
        && events.iter().collect::<std::collections::HashSet<_>>().len() == events.len()
}

Prevention

When it happens

Trigger: Constructing NotificationConfigUpdate::EventSoundEvents with a list containing an unrecognized event string (e.g. a typo like "turn_complte") or a duplicated name, then calling validate() via value(), persist_for_profile(), edit_extras(), or apply_update().

Common situations: Hand-edited config with misspelled event names; case mismatches (parser is exact); a generator appends events without checking for existing entries, producing duplicates; event names renamed between versions.

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 Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/1f1d237a2604db05. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/notifications.rs:313

    pub fn validate(&self) -> Result<()> {
        match self {
            Self::ThresholdSecs(v) | Self::EventSoundMinIntervalMs(v) => anyhow::ensure!(
                *v <= i64::MAX as u64,
                "notification integer exceeds TOML range"
            ),
            Self::SoundFile(path) => {
                let value = path.to_str().context("sound path must be UTF-8")?;
                anyhow::ensure!(
                    !value.is_empty()
                        && value.len() <= 4096
                        && !value.chars().any(char::is_control),
                    "invalid sound path"
                );
            }
            Self::EventSoundEvents(events) => {
                anyhow::ensure!(events.len() <= 6, "too many notification events");
                for (index, event) in events.iter().enumerate() {
                    anyhow::ensure!(
                        NotificationEvent::parse(event).is_some()
                            && !events[..index].contains(event),
                        "invalid or duplicate notification event"
                    );
                }
            }
            _ => (),
        }
        Ok(())
    }
    pub fn value(&self) -> Result<Option<toml::Value>> {
        self.validate()?;
        Ok(Some(match self {
            Self::Method(value) => value.as_str().into(),
            Self::Sound(Some(value)) | Self::CompletionSound(value) => value.as_str().into(),
            Self::Sound(None) => return Ok(None),
            Self::Condition(value) => value.as_str().into(),
            Self::SubagentCompletion(value) => value.as_str().into(),

View on GitHub (pinned to 73e0f67d83)