Skip to content

Notification timeout violates freedesktop spec: critical not sticky + expire_timeout=0 ignored by default #2601

@simonbcn

Description

@simonbcn

Summary

Two violations of the freedesktop Desktop Notifications Specification, both caused by the same function (calculateDuration in Services/System/NotificationService.qml), filed together.

1. expire_timeout=0 is ignored unless respectExpireTimeout is enabled

The spec is unambiguous on this (Notify method, section 9):

If -1, the notification's expiration time is dependent on the notification server's settings, and may vary for the type of notification. If 0, never expire.

https://specifications.freedesktop.org/notification-spec/latest/protocol.html

Current behavior in NotificationService.qml:

function calculateDuration(data) {
  const durations = [..., ..., ...];
  if (Settings.data.notifications?.respectExpireTimeout) {
    if (data.expireTimeout === 0) return -1;
    if (data.expireTimeout > 0)   return data.expireTimeout;
  }
  return durations[data.urgency];
}

respectExpireTimeout defaults to false, so a client calling notify-send -t 0 (an explicit, documented request to never expire) is silently overridden by the urgency-based fallback. 0 is a contract defined by the spec, not a hint — clients use it precisely when they need persistence.

This was already raised in #419 and closed as fixed, but the underlying behavior is unchanged in 4.7.6 — only the opt-in toggle was added.

Proposed fix: always honor expire_timeout == 0 (never expire), even when respectExpireTimeout is false. The toggle can keep controlling positive timeouts for users who want server-side overrides, but 0 must not be overridable.

2. urgency=critical is not sticky by default

Also a spec violation, this time of section 7 (Urgency Levels):

Critical notifications should not automatically expire, as they are things that the user will most likely want to know about. They should only be closed when the user dismisses them, for example, by clicking on the notification.

https://specifications.freedesktop.org/notification-spec/latest/urgency-levels.html

Apps that rely on this (pacman post-transaction reboot warnings, system update tools, low-battery alerts, etc.) send -u critical expecting persistence. Noctalia auto-dismisses them after criticalUrgencyDuration seconds (default 15s), which can cause real reboot/upgrade warnings to be missed.

For reference, all major implementations follow the spec here: dunst, mako, swaync, GNOME Shell, KDE Plasma, notify-osd.

Proposed fix: treat urgency=critical as sticky by default — durations[2] returns -1 unless the user explicitly configures a finite duration. Equivalent alternative: default criticalUrgencyDuration: 0 with 0 meaning "never expire", clearly documented in the settings UI.

Concrete reproducer (point 1)

Default config (respectExpireTimeout: false):

notify-send -t 0 -u critical "Test" "Should stay until dismissed"

The notification disappears after criticalUrgencyDuration seconds (15s by default) instead of staying. Both points fail in this single command.

Environment

  • noctalia-shell 4.7.6-1 (Arch)
  • niri 26.04
  • Affected file: Services/System/NotificationService.qml, function calculateDuration (~ line 398)

Why one issue instead of two

Both points hit the same function and the same default-behavior philosophy ("server overrides client"). Fixing them together avoids two rounds of breaking-default discussion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions