Skip to content

Notifications silently fail under Claude Code: hook subprocess has no controlling TTY, OSC 777 write to /dev/tty is dropped #47

Description

@Zmetser

Summary

When the plugin runs under Claude Code (CLAUDECODE=1), scripts/warp-notify.sh writes the OSC 777 escape sequence to /dev/tty, but Claude Code spawns hook subprocesses without a controlling TTY, so the write fails silently and no notification ever reaches Warp.

  • Plugin version: 2.0.0
  • Warp version: v0.2026.05.13.09.15.stable_01 (also reproduced on v0.2026.05.06.15.42.stable_04)
  • Claude Code: 2.1.142 on macOS (Darwin 25.3.0)

Repro

  1. Install the plugin in Claude Code, restart / /reload-plugins.
  2. Confirm WARP_CLI_AGENT_PROTOCOL_VERSION=1 and WARP_CLIENT_VERSION are set (they are).
  3. Confirm Warp's "Receive desktop notifications" + agent toggles are on.
  4. Run anything in Claude Code that triggers Stop, PermissionRequest, or idle Notification hooks.
  5. Expected: native Warp/macOS notification. Actual: nothing fires.

Root cause

Adding debug logging to warp-notify.sh shows every hook firing with:

/dev/tty: Device not configured
  /dev/tty exists but NOT writable
  tty: not a tty
  printf to /dev/tty FAILED

The hook subprocess Claude Code spawns has no controlling terminal, so > /dev/tty errors. The 2>/dev/null || true swallows the error, so the failure is invisible.

Workaround / suggested fix

Walk up the process tree to find the nearest ancestor with a real tty (the parent claude process keeps its tty, e.g. ttys003), and write the OSC sequence to that device directly. Patch that works locally:

find_parent_tty() {
    local pid=$PPID
    local tty
    while [ -n "$pid" ] && [ "$pid" != "1" ] && [ "$pid" != "0" ]; do
        tty=$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ')
        if [ -n "$tty" ] && [ "$tty" != "??" ] && [ "$tty" != "-" ]; then
            echo "/dev/$tty"
            return 0
        fi
        pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ')
    done
    return 1
}

TARGET_TTY=$(find_parent_tty)
if [ -n "$TARGET_TTY" ] && [ -w "$TARGET_TTY" ]; then
    printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > "$TARGET_TTY" 2>/dev/null || true
else
    printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > /dev/tty 2>/dev/null || true
fi

With this in place, Stop / PermissionRequest / idle notifications all fire correctly.

Minor

The resulting notifications show a generic folder icon rather than the Warp icon — not sure if that's a separate macOS notification-attribution issue or related to the OSC 777 path.

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