Windows: Channels (Telegram plugin) unstable after reboot — zombie processes, port conflicts, exit code -1
Summary
Running Claude Code with --channels plugin:telegram@claude-plugins-official on Windows as an always-on service (via Task Scheduler) is extremely unreliable. After every system reboot, users must manually fight zombie processes and port conflicts before the channel can resume. Even when the wrapper script handles cleanup, claude.exe itself frequently crashes with exit code -1 with no useful diagnostics.
Environment
- OS: Windows 11 Pro (10.0.26200)
- Claude Code: Latest (installed via
~/.local/bin/claude.exe) - Channel:
plugin:telegram@claude-plugins-official - Permission mode:
bypassPermissions - Launch method: Windows Task Scheduler → VBS silent launcher → CMD wrapper script
- Upstream dependency: OpenClaw Gateway on port 25525 (TCP)
Problems observed
1. claude.exe exits with code -1 (no diagnostics)
After running for 1-2 hours, claude.exe silently exits with code -1. No crash dump, no error message, no useful log output. This happens repeatedly:
[2026-04-02 04:22:55] Starting claude.exe --channels plugin:telegram@claude-plugins-official
[2026-04-02 05:55:59] claude.exe exited with code -1
[2026-04-02 05:56:47] Starting claude.exe (restart attempt)
[2026-04-02 06:14:56] claude.exe exited with code -1
Earlier logs (3/27) show the same pattern, sometimes with triple-duplicate crash entries suggesting a race condition in the exit handler:
[2026-03-27 05:35:04] claude.exe exited with code -1
[2026-03-27 05:35:06] claude.exe exited with code -1
[2026-03-27 05:35:06] claude.exe exited with code -1
2. Zombie child processes (bun.exe) not cleaned up on exit
When claude.exe exits (normally or abnormally), the spawned bun.exe process running the Telegram MCP server (telegram/server.ts) is not terminated. This causes:
- Port conflicts: The next
claude.exeinstance can't bind the same port getUpdatesrace condition: Multiple Telegram pollers fight over the same bot token- Accumulated zombies: After a few crash-restart cycles, multiple
bun.exeinstances pile up
We had to build our own cleanup routine:
Get-CimInstance Win32_Process -Filter "Name='bun.exe'" |
Where-Object { $_.CommandLine -match 'telegram.*server\.ts' } |
ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
3. No built-in single-instance guard
Running channels via Task Scheduler (triggered on logon) can spawn duplicate claude.exe --channels instances. Claude Code has no mechanism to detect an existing channel session and either reuse it or cleanly replace it.
We built a 230-line CMD wrapper (v4.1) with PID-based locking, WebSocket health checks, exponential backoff, and zombie cleanup — all of which should arguably be handled by Claude Code itself.
4. No channel health monitoring
There's no way to query whether a running channel is healthy (connected to upstream, processing messages). Our wrapper has to check for active WebSocket connections to the gateway port via Get-NetTCPConnection as a proxy for health — a fragile heuristic.
What we've built to work around this
Our wrapper script (claude-channel-start.cmd v4.1) implements:
- PID-based lock file with write-then-verify (race condition prevention)
- Gateway TCP probe with 5-minute timeout (30 retries × 10s)
- WebSocket health check to distinguish "alive but stuck" from "alive and working"
- Zombie
bun.execleanup before start, after exit, and before restart - Exponential backoff: 15s → 30s → 60s → 120s → 240s (capped at 300s)
- Failure limit (5 consecutive crashes → stop and alert)
- Log rotation at 100KB
This works, but it's 230 lines of CMD scripting to paper over gaps that should be handled by Claude Code's process lifecycle.
Log timeline showing the evolution of pain
| Date | Version | Issue |
|------|---------|-------|
| 3/22 | v2.0 | Duplicate startups, no guard |
| 3/27 | v2.0 | Race condition: triple-duplicate crash logs, triple-duplicate restarts |
| 3/27 | v3.x | Process-count guard broken — stuck in "Aborted" loop for 8+ hours |
| 3/29 | v4.0 | PID lock fixed duplicates, but exit code -1 crashes continue |
| 4/02 | v4.1 | Same exit code -1 pattern, now with proper backoff |
Feature requests
- Graceful child process cleanup: When
claude.exeexits, kill all spawned MCP server subprocesses (bun, node, etc.) - Built-in single-instance guard:
--channelsmode should detect and handle existing instances (lock file, named pipe, etc.) - Health endpoint: Expose a local HTTP or named-pipe health check for channel status (connected, message count, uptime)
- Structured exit codes: Differentiate between "plugin failed to load", "auth error", "network timeout", "OOM", etc. instead of blanket
-1 - Windows service/daemon documentation: Official guidance for running channels as always-on services on Windows (Task Scheduler, NSSM, etc.)
--restart-on-failureflag: Built-in auto-restart with configurable backoff, so users don't need wrapper scripts
Reproduction
claude.exe --channels plugin:telegram@claude-plugins-official --permission-mode bypassPermissions
Run this via Task Scheduler on Windows. Reboot. Observe:
- Zombie
bun.exefrom previous session still holding resources - New
claude.exemay fail silently or conflict with zombies - Exit code -1 after 1-2 hours of operation with no diagnostic output
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗