fix(worker): report startup port conflicts instead of crashing silently

Mutex was Local\ (per Windows session), so a second RDP/fast-user-switching
session bypassed the single-instance guard. A Kestrel bind failure on top of
that had no try/catch, so a second worker (or any process on 47821/47822)
died as an unhandled crash with nothing in the log to explain why the UI
never connects. Mutex is now Global\, bind failures and any last-resort
unhandled exception log a Fatal line and exit(1) instead of crashing raw.
This commit is contained in:
mika kuns
2026-09-10 13:12:25 +02:00
parent 1177463a82
commit c9d4823f06
3 changed files with 85 additions and 8 deletions
@@ -0,0 +1,19 @@
namespace ClaudeDo.Worker.Lifecycle;
/// <summary>
/// Formats the Fatal log line for a startup/bind failure (e.g. a port already in use) so
/// Program.cs can log a diagnosable message and exit cleanly instead of crashing unhandled.
/// </summary>
public static class WorkerStartupFailure
{
public const int ExitCode = 1;
public static string FormatBindFailureMessage(Exception ex, int signalRPort, int externalMcpPort)
{
var ports = externalMcpPort > 0
? $"ports {signalRPort} and {externalMcpPort}"
: $"port {signalRPort}";
return $"ClaudeDo.Worker failed to start on {ports}: {ex.Message} " +
"Is another worker instance already running, or is another process using this port?";
}
}