Skip to content

Desktop notifications never write to the TUI terminal - #1235

Merged
trissim merged 2 commits into
mainfrom
fix/notify-stderr
Oct 25, 2026
Merged

trissim merged 2 commits into
mainfrom
fix/notify-stderr

Conversation

@trissim

@trissim trissim commented Oct 25, 2026 •

Copy link
Copy Markdown

Toad froze for Tristan with its screen covered in Failed to show notification: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name is not activatable, one line per bus message.

Cause: notifypy chooses its notify-send path whenever notify-send is installed (notifypy/notify.py:136-140). That path runs subprocess.check_output with stderr inherited (notifypy/os_notifiers/linux.py:86), so with no notification daemon running, notify-send's error lands on Toad's terminal once per notification.

Fix, at the owner (NotificationPolicy in setting_choices.py):

  • On Linux, use notifypy's D-Bus notifier through its declared use_custom_notifier hook. It starts no child process, so nothing can write over the TUI.
  • That notifier reports a D-Bus error reply as delivered (it never checks the reply type), so UncheckedDBusDesktop asks the session bus once whether org.freedesktop.Notifications is owned or activatable. If neither, the policy becomes AbsentDesktop: one in-app warning, and no further attempts for the session.
  • jeepney==0.9.0 (linux) is declared, since Toad now imports it directly. uv.lock is updated by hand because uv isn't available on this machine.

Manual check (installed runtime interpreter, this branch's src, Python stderr captured the way Textual captures it, fd 2 sent to a file):

  • main, notify-send path: send -> False and fd 2 got Failed to show notification: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name is not activatable
  • this branch, 3 deliveries: state AbsentDesktop, 1 in-app notice, fd 2 0 bytes

There's no new test: it's a trivial edit, per Tristan.

Platform family (send-back fix, 051b834)

The first head chose the notifier with toad.os == "linux" and the app name with toad.os == "macos" (IMPL-1). toad.Platform is now one family decoded once from platform.system(): the base answers desktop() -> DesktopService(), application_name "Toad" and select(matrix) (exact key or "*"); LinuxPlatform overrides desktop() with UncheckedDBusDesktop(), MacPlatform overrides the name. Deleted: both compares, _OS_map, toad.os, get_os_matrix (a second copy of commands_for's selection), the second OS literal in agent_schema, and agent_modal's cast. The catalog's TOML key stays a string, as the external format, carried once per member as key.

Interpreter check on this machine: toad.platform -> LinuxPlatform, key linux, name "Toad"; NotificationPolicy.desktop -> UncheckedDBusDesktop; notifypy is not imported by import toad or setting_choices. No tests run (owner order).

🤖 Generated with Claude Code

https://claude.ai/code/session_013ejYrMLnLWah8wCDDcb57V

trissim and others added 2 commits October 25, 2026 02:46
notifypy picks its notify-send path whenever notify-send is installed, and
that path runs check_output with stderr inherited, so with no notification
daemon every bus message printed "Failed to show notification: GDBus.Error
... ServiceUnknown" straight over Toad's screen. On Linux the policy now uses
notifypy's D-Bus notifier, which starts no child process. That notifier
reports a D-Bus error reply as a delivery, so the policy asks the session bus
once whether org.freedesktop.Notifications is owned or activatable; when it is
neither, the policy becomes AbsentDesktop, shows one in-app notice, and stops
trying for the session. jeepney is declared, since Toad now imports it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ejYrMLnLWah8wCDDcb57V
@trissim
trissim marked this pull request as ready for review October 25, 2026 06:49
@trissim
trissim enabled auto-merge October 25, 2026 06:49
Copilot AI balanced review requested due to automatic review settings October 25, 2026 06:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@trissim
trissim merged commit 96c8e21 into main Oct 25, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants