Skip to content

fix(activator): guard Terminal.app tab enumeration against tab-less windows - #295

Open
kuznetsov-m wants to merge 1 commit into
wxtsky:mainfrom
kuznetsov-m:main
Open

fix(activator): guard Terminal.app tab enumeration against tab-less windows#295
kuznetsov-m wants to merge 1 commit into
wxtsky:mainfrom
kuznetsov-m:main

Conversation

@kuznetsov-m

Copy link
Copy Markdown

Summary

Clicking a session that lives in anything but the first Terminal.app window did
nothing at all — no tab switch, no window raise. The Terminal.app activation
script aborts halfway through whenever windows contains a window whose tabs
can't be read.

Fixes #294.

Root cause

Sources/CodeIsland/TerminalActivator.swiftactivateTerminalApp(ttyPath:cwd:).
All three matching strategies iterate windows like this:

repeat with w in windows
    repeat with t in tabs of w      -- not guarded
        try
            if tty of t is targetTty thenend try
    end repeat

The try covers the comparison but not tabs of w. Terminal.app can keep a
window that has no tabs at all — an invisible utility window (name "",
visible false) that lives for the whole lifetime of the Terminal process.
Asking it for tabs raises -10000, which tears down the entire
tell application "Terminal" block. Two consequences:

  1. Every window enumerated after that one is skipped, so its session is never
    found by any of the three strategies.
  2. The final activate on line 772 is never reached either — unlike the iTerm2
    and Ghostty branches, this block has no outer try, so the failure also
    costs the app-level raise that would otherwise be the visible fallback.

Observed state (macOS 14.1, Terminal.app 2.14):

count windows → 3
window 1: id=34582 visible=true  tabs=1   (session A, /dev/ttys136)
window 2: id=591   visible=false tabs=ERR ← aborts the loop here
window 3: id=34663 visible=true  tabs=1   (session B, /dev/ttys138)

Running the enumeration exactly as the app does it:

/dev/ttys136 → found=true
/dev/ttys138 → FAILED: Terminal got an error: AppleEvent handler failed.

From the user's side this is indistinguishable from the Strategy 1 misfire
fixed in #124: one session jumps, the others silently do nothing.

Fix

Wrap each window's tab enumeration in its own try, in all three strategies —
the same hardening #202 applied to iTerm2's select <window>.

Semantics are unchanged: exit repeat still refers to the tab loop, and
if found then exit repeat stays outside the guard, so the outer loop still
stops at the first match. Only windows that cannot be read are skipped.

Why not filter on visible of w

The issue originally suggested skipping invisible windows, and this PR
deliberately doesn't. Minimized windows are tracked by the separate
miniaturized property, and the comment on lines 775–777 documents that on some
macOS 14 builds minimized windows fall out of windows entirely — a visibility
filter would risk regressing the deminiaturize path added for #124. A bare try
skips exactly the unreadable windows and nothing else.

Test plan

  • Extracted the patched AppleScript from the Swift literal with real values
    substituted (/dev/ttys136, projects) and compiled it with osacompile
    — nesting of try/repeat is valid
  • Ran the patched script against a live Terminal.app: no error, correct tab
    selected, Terminal raised
  • Verified the pre-patch script fails on the same window set with
    AppleEvent handler failed while the patched one returns found=true
  • swift build

Related

The iTerm2 branches (lines 588, 617, 653) still enumerate tabs unguarded. They
have an outer try, so a bad window can't break the app — it just silently ends
the search. Happy to fold that into this PR if you'd prefer one sweep.

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.

Terminal.app: window enumeration aborts on a tab-less invisible window (-10000), sessions in later windows are never focused

1 participant