PushNotification returns success but the phone push arrives ~23% of the time

Status Open
Reported on v2.1.226
Maintainer reply None cached
Activity 0 comments · opened Aug 9, 2026

PushNotification reports success but the phone push arrives ~23% of the time

Summary

PushNotification returns Terminal notification sent. Mobile push requested. on every
call, but the notification reaches the phone on only a minority of calls. Across 13 sends
in one session, 3 arrived. There is no error, no differing return string, and no local log
recording the attempt, so a caller cannot tell a delivered push from a lost one.

The failures are not iOS foreground suppression and not a disconnected phone. Both were
tested and ruled out — details below.

Environment

  • Claude Code 2.1.226
  • macOS 26.5.2 (build 25F84), Darwin 25.5.0
  • Model: claude-opus-5[1m]
  • Terminal: Ghostty
  • Relevant settings in ~/.claude/settings.json:
  • env.CLAUDE_CODE_DISABLE_NOTIFICATION_PRESENCE_CHECK: "1" (verified live in the

session's tool environment)

  • remoteControlAtStartup: true
  • agentPushNotifEnabled: true
  • inputNeededNotifEnabled: true
  • preferredNotifChannel: "iterm2_with_bell"

What happened

13 PushNotification calls, all with status: "proactive", spread over roughly an hour
in a single session. Every call returned the identical string:

Terminal notification sent. Mobile push requested.

Arrival was confirmed by the phone's owner after each send.

| # | Message | Reached phone |
|---|---------|---------------|
| 1 | transmit pane relay test - please confirm receipt | no |
| 2 | transmit pane relay test 2 - please confirm receipt | yes |
| 3 | From Codex, via the herdr skill. | no |
| 4 | From Claude, via cross-session messaging. | no |
| 5 | From Claude, via the herdr skill. | no |
| 6 | Direct send from your active Claude session - did this one arrive? | yes |
| 7 | Retry from Codex via herdr. | no |
| 8 | Same test as the last one, my wording this time - the Codex/herdr retry. Did it land? | no |
| 9 | (identical text to #8, sent immediately after) | yes |
| 10 | App-open test - you should get this one while the Claude app stays running. | no — Claude app was in the foreground, expected iOS behavior |
| 11 | Terminal-active test - sent while you were typing here. | no |
| 12 | Send 13 - checking whether the phone route is up right now. | no |
| 13 | Send 14 - back-to-back with the last one. | no |

3 of 13 arrived. Excluding #10, which has a known explanation, 3 of 12.

What was ruled out

Message content, wording, and who requested the send. #8 and #9 were byte-identical
text sent back to back in the same session. #8 never arrived; #9 did. Sends were variously
composed by the model, supplied verbatim by the user, and relayed from a peer Claude
session — the split does not follow any of those.

iOS foreground suppression. Confirmed real and confirmed to explain only #10. Banners
appear normally when the phone is on the home screen. Sends #1, #3, #4, #5, #7, #8, #11,
#12 and #13 happened with the phone on the home screen and produced no banner.

Delivered-but-unseen. The missing notifications are absent from iOS Notification
Center, not merely missed as banners.

Disconnected phone / Remote Control down. Send #13 is the clearest case. The request
for #13 was typed on the phone and reached the Claude Code session, so the phone-to-session
leg was demonstrably working seconds before the session-to-phone push vanished.

Rate limiting or a warm-up effect. #9 arrived immediately after #8 failed, which is
backwards for a throttle. Failures occur both in bursts and after long quiet stretches.

The presence check. Disabled via CLAUDE_CODE_DISABLE_NOTIFICATION_PRESENCE_CHECK=1
and verified present in the tool environment. The tool never emitted its
Not sent — this terminal is active response, consistent with the check being off.

Expected

Either the push arrives, or the return value distinguishes a failed or undeliverable push
from a successful one.

Actual

The return string is constant regardless of outcome. Roughly three quarters of pushes are
lost silently. Nothing on the local machine records the attempt: ~/.claude/debug/ has no
file newer than a week before the session, and ~/.claude/daemon/dispatch/ is empty.

Impact

PushNotification cannot be relied on to reach a user who has stepped away, and neither
the agent nor the user can tell which sends were lost. An agent that pauses for a decision
and signals with a push has no way to know the signal never landed.

Suggested fix

Surface the delivery result in the tool's return value — at minimum, distinguish
"push accepted by the service" from "no connected device" from "send failed" — and log push
attempts locally so the failure is diagnosable without instrumenting the client.

View original on GitHub ↗