Summary
gobby/agents/tmux/pty_bridge.py imports fcntl and termios at module scope. Both are POSIX-only, so the daemon cannot start on Windows — the import chain runs during gobby start and dies before the runner comes up.
Still present on main @ 45b0ea0: pty_bridge.py#L11-L18.
Traceback
File "...\gobby\runner.py", line 219, in __init__
from gobby.runner_init import (
File "...\gobby\runner_init\__init__.py", line 6, in <module>
from gobby.runner_init.orchestration import init_orchestration
File "...\gobby\runner_init\orchestration.py", line 9, in <module>
from gobby.agents.lifecycle_monitor import AgentLifecycleMonitor
File "...\gobby\agents\lifecycle_monitor.py", line 28, in <module>
from gobby.agents.tmux.session_manager import TmuxSessionManager
File "...\gobby\agents\tmux\__init__.py", line 23, in <module>
from gobby.agents.tmux.pty_bridge import TmuxPTYBridge
File "...\gobby\agents\tmux\pty_bridge.py", line 14, in <module>
import fcntl
ModuleNotFoundError: No module named 'fcntl'
gobby start reports Health check failed and the daemon exits; the traceback lands in ~/.gobby/logs/gobby-error.log.
Reproduce
On Windows: uv tool install gobby then gobby start.
Root cause
gobby/agents/tmux/__init__.py unconditionally imports TmuxPTYBridge, and that module imports fcntl/termios at the top. Since lifecycle_monitor pulls in the tmux package during daemon init, there is no way to reach the runner on a platform without those modules.
Both are only used inside methods — fcntl.ioctl(..., termios.TIOCSWINSZ, ...) at lines 82 and 177 — so nothing at import time needs them.
Suggested fix
Guard the import; PTY bridging is inherently POSIX-only and can fail at call time instead of import time:
import asyncio
-import fcntl
import logging
import os
import struct
-import termios
from dataclasses import dataclass, field
from datetime import UTC, datetime
+try: # POSIX-only: tmux PTY bridging is unavailable on Windows
+ import fcntl
+ import termios
+except ImportError: # pragma: no cover - Windows
+ fcntl = None # type: ignore[assignment]
+ termios = None # type: ignore[assignment]
+
from gobby.config.tmux import TmuxConfig
Ideally the two ioctl call sites would also raise a clear error when fcntl is None, rather than an AttributeError.
Note
Fixing this alone is not sufficient to start the daemon on Windows — the next blocker is loop.add_signal_handler raising NotImplementedError, filed separately.
Windows isn't listed as a supported platform in the README, but gobby/agents/tmux/wsl_compat.py (needs_wsl, convert_windows_path_to_wsl) suggests it's at least partly intended. Happy to open a PR if a Windows daemon is in scope.
Environment
- gobby 0.4.6 (installed via
uv tool install)
- Python 3.13 (CPython, windows-x86_64)
- Windows 11 Pro 10.0.26220
Summary
gobby/agents/tmux/pty_bridge.pyimportsfcntlandtermiosat module scope. Both are POSIX-only, so the daemon cannot start on Windows — the import chain runs duringgobby startand dies before the runner comes up.Still present on
main@45b0ea0:pty_bridge.py#L11-L18.Traceback
gobby startreportsHealth check failedand the daemon exits; the traceback lands in~/.gobby/logs/gobby-error.log.Reproduce
On Windows:
uv tool install gobbythengobby start.Root cause
gobby/agents/tmux/__init__.pyunconditionally importsTmuxPTYBridge, and that module importsfcntl/termiosat the top. Sincelifecycle_monitorpulls in the tmux package during daemon init, there is no way to reach the runner on a platform without those modules.Both are only used inside methods —
fcntl.ioctl(..., termios.TIOCSWINSZ, ...)at lines 82 and 177 — so nothing at import time needs them.Suggested fix
Guard the import; PTY bridging is inherently POSIX-only and can fail at call time instead of import time:
Ideally the two
ioctlcall sites would also raise a clear error whenfcntl is None, rather than anAttributeError.Note
Fixing this alone is not sufficient to start the daemon on Windows — the next blocker is
loop.add_signal_handlerraisingNotImplementedError, filed separately.Windows isn't listed as a supported platform in the README, but
gobby/agents/tmux/wsl_compat.py(needs_wsl,convert_windows_path_to_wsl) suggests it's at least partly intended. Happy to open a PR if a Windows daemon is in scope.Environment
uv tool install)