Skip to content

Do not hand Thunderbird the caller's stdout (v1.2.1) - #2

Open
U-C4N wants to merge 1 commit into
mainfrom
fix/launch-inherits-stdout
Open

Do not hand Thunderbird the caller's stdout (v1.2.1)#2
U-C4N wants to merge 1 commit into
mainfrom
fix/launch-inherits-stdout

Conversation

@U-C4N

@U-C4N U-C4N commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Found while installing the 1.2.0 add-on on a real machine: the install succeeded, and the command never returned.

_launch in addon_install.py redirected only stdin, so the Thunderbird it starts inherited our stdout and stderr. That process outlives us by design, so any caller capturing our output — an agent, a CI step, a shell pipeline — stays blocked until the mail client is closed. Their own timeout does not rescue them: killing the direct child leaves the grandchild holding the pipe.

A reduced repro had the caller waiting the launched process's full 45s lifetime despite a 12s timeout.

close_fds does not cover the standard handles, and DETACHED_PROCESS only detaches the console — both streams have to be redirected explicitly. The daemon spawn in bridge.py already did exactly that; this is the same requirement one file over, and it was the only remaining Popen in the package without it.

The regression test drives _launch from a helper script that exits immediately and asserts the caller is released. It fails in 25s against the old code and passes in 0.4s with the fix.

Versions bumped to 1.2.1 across pyproject.toml, addon/manifest.json, and server.py's fallback, per the unified-version rule the drift test enforces.

195 tests.

🤖 Generated with Claude Code

`_launch` redirected only stdin, so the Thunderbird it starts inherited our
stdout and stderr. Since that process outlives us by design, any caller
capturing our output — an agent, a CI step, a shell pipeline — stays blocked
long after `install-addon` has finished its work. Their own timeout does not
help: killing us leaves the grandchild holding the pipe.

Observed while installing the 1.2.0 add-on: the install succeeded and the
command never returned. A reduced repro had the caller waiting the full
lifetime of the launched process, 45s, despite a 12s timeout.

`close_fds` does not cover the standard handles and DETACHED_PROCESS only
detaches the console, so both have to be redirected explicitly — which the
daemon spawn in bridge.py already did. Same requirement, one file over.

The regression test fails in 25s against the old code and passes in 0.4s
with the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant