URL-opened apps freeze on stdout/stderr writes; open.zig leaks pipe read-ends
#12767
Issue Description
try exe.collectOutput(alloc, &stdout, &stderr, 50 * 1024);
_ = try exe.wait();
Inverse of #5999 — same trade-off, other direction. Fire-and-forget didn't leak FDs but left zombies; the current code reaps cleanly but leaks pipes. Expected BehaviorApps launched via ghostty's URL-open path keep running normally, regardless of how much they write to stdout/stderr. Actual BehaviorThe launched app freezes after roughly 64 KiB of cumulative stdout/stderr output. Symptom in the wild: Firefox going unresponsive ~3 days after I opened a GitHub link via xdg-open from a ghostty pane. Its main thread was on wchan Each URL-open also leaves two Reproduction StepsTk script that writes stdout from its main thread (the #!/usr/bin/env -S uv run --script --python-preference only-managed
# /// script
# requires-python = ">=3.11"
# ///
"""Save as /tmp/noisy_child.py and chmod +x."""
import os, sys, time, tkinter as tk
root = tk.Tk()
root.title("ghostty pipe leak repro")
label = tk.Label(root, font=("monospace", 18))
label.pack(padx=20, pady=20)
start, i = time.monotonic(), 0
while True:
sys.stdout.write(f"[t={time.monotonic()-start:8.3f}s i={i:09d}] " + "x"*256 + "\n")
sys.stdout.flush()
label.config(text=f"i={i:,}\npid={os.getpid()}")
root.update()
i += 1# 1) Register it as the handler for ghostbug:// URLs.
cat > ~/.local/share/applications/ghostty-pipe-leak.desktop <<'EOF'
[Desktop Entry]
Type=Application
Name=Ghostty Pipe Leak Repro
Exec=/tmp/noisy_child.py %u
NoDisplay=true
MimeType=x-scheme-handler/ghostbug;
EOF
update-desktop-database ~/.local/share/applications 2>/dev/null
xdg-mime default ghostty-pipe-leak.desktop x-scheme-handler/ghostbug
# 2) In a ghostty pane, print and ctrl-click an OSC 8 link.
printf '\033]8;;ghostbug://test\033\\click\033]8;;\033\\\n'The window ticks for ~1–2 s then freezes mid-update. Verify: VPID=$(pgrep -af '/tmp/noisy_child.py' | awk '/python/{print $1; exit}')
cat /proc/$VPID/wchan # anon_pipe_write
GPID=$(pgrep -x ghostty | head -1)
for f in 1 2; do
ino=$(readlink /proc/$VPID/fd/$f | sed 's/.*\[\(.*\)\].*/\1/')
for gfd in /proc/$GPID/fd/*; do
[ "$(readlink $gfd 2>/dev/null)" = "pipe:[$ino]" ] || continue
echo "victim FD=$f <- ghostty FD=$(basename $gfd) flags=$(awk '/^flags/{print $2}' /proc/$GPID/fdinfo/$(basename $gfd))"
done
done
# victim FD=1 <- ghostty FD=N flags=02000000 (O_RDONLY|O_CLOEXEC = leaked read end)
# victim FD=2 <- ghostty FD=M flags=02000000Suggested Fix
exe.stdout_behavior = .Ignore;
exe.stderr_behavior = .Ignore;
Ghostty LogsNone. Ghostty VersionGhostty ConfigurationNot relevant — reproduces with default config and any keybind that triggers Additional Relevant Configurationxdg-utils AI disclosure (per AI_POLICY.md): investigation, reproducer script, and this writeup were drafted with Claude Code; diagnosis was verified empirically against my running ghostty instance (matched wchan, FD inodes, ghostty FD-table state). I've read the draft end to end. |
Replies: 1 comment
|
Since you're using Hyprland, you probably don't have the various DBus portals set up that https://github.com/ghostty-org/ghostty/blob/c5946f4fefd9382ba00d21c1bed0b6ff33a1b687/src/apprt/gtk/portal/OpenURI.zig So, basically, Firefox should never be a child process of Ghostty and Ghostty shouldn't need to worry about dealing with lots of output from long-running processes (that aren't the shell) in this way. In any case, Ghostty should probably deal with this more robustly. If you have a development environment it would be good if you could try out this PR: |
Since you're using Hyprland, you probably don't have the various DBus portals set up that
xdg-opencan take advantage of to launch URLs.xdg-open(and any commands that it runs) shouldn't be hanging around for more than the few milliseconds it takes to pass the command to DBus. In fact Ghostty can take advantage of those DBus portals directly (at least for non-file URLs) and never directly executexdg-open:https://github.com/ghostty-org/ghostty/blob/c5946f4fefd9382ba00d21c1bed0b6ff33a1b687/src/apprt/gtk/portal/OpenURI.zig
https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.OpenURI.html#org-freedesktop-portal-openu
So, basically, Firefox should never be a child pr…